Contents
Introduction
I am a huge fan of the popular itsutils collection of command-line tools. They allow you to control your docked Windows Mobile device from your PC's command prompt. This article covers my concept for an addition to this toolset which brings a background application window to the foreground. I find this to be more convenient than clicking through the Start-menu to Task Manager, locating my executable in the running application list, and selecting switch to from the drop-down menu.
pfront Usage
The pfront tool usage is similar to the other itsutil tools. From your Windows PC's command prompt: pfront.exe application_name.exe. If application_name.exe is the name of an application that is already running on your Windows Mobile device, its window will appear in the foreground. The itsutil command pps will give you a list of all the processes in the system if you don't know whether or not your application is running.
pfront Design
Like all itsutils, pfront is divided into two parts: the Windows executable (pfront.exe) and a Windows Mobile DLL (pfront_lib.dll).
pfront.exe
The pfront executable uses the RAPI2 interface to upload the pfront_lib.dll library to the mobile device and invokes its exported RAPI extension method. Connecting to the attached Windows Mobile device using RAPI2 device is a brief 9 lines of code:
CComPtr< IRAPIDesktop > rapi_desktop;
rapi_desktop.CoCreateInstance( CLSID_RAPI );
CComPtr< IRAPIEnumDevices > rapi_device_list;
rapi_desktop->EnumDevices( &rapi_device_list );
CComPtr< IRAPIDevice > rapi_device;
rapi_device_list->Next( &rapi_device );
CComPtr< IRAPISession > rapi_session;
rapi_device->CreateSession( &rapi_session );
rapi_session->CeRapiInit();
Once connected, we use CeCreateFile
and CeWriteFile
to copy pfront_lib.dll to the mobile device. In the code below, I use boost::shared_ptr<>
to encapsulate the HANDLE
returned by CeCreateFile
If you don't use boost, it won't hurt to use a naked HANDLE
as you normally would.
static BOOL CopyToDevice( IRAPISession* session,
const wchar_t* local_src_file,
const wchar_t* remote_dest_file )
{
BOOL success = FALSE;
std::ifstream local( local_src_file, std::ios_base::binary );
if( local.is_open() )
{
boost::shared_ptr< void > remote_file(
session->CeCreateFile( remote_dest_file,
GENERIC_WRITE,
0,
NULL,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
INVALID_HANDLE_VALUE ),
boost::bind( &IRAPISession::CeCloseHandle, session, _1 ) );
if( INVALID_HANDLE_VALUE != remote_file.get() )
{
char buffer[ 10 * 1024 ];
DWORD bytes_written;
while( local.good() )
{
local.read( buffer, sizeof( buffer ) );
if( !session->CeWriteFile( remote_file.get(),
buffer,
local.gcount(),
&bytes_written,
NULL ) )
{
break;
}
success = local.eof();
}
}
else
{
success = ( session->CeGetLastError() == ERROR_FILE_EXISTS );
}
}
return success;
};
Now that the RAPI extension DLL is uploaded to the mobile device, it is a simple matter of calling CeRapiInvoke
to invoke the exported RAPI method in the DLL and pass it the name of the executable specified by the user in the command line argument.
BYTE *unused_a = NULL;
DWORD unused_b = 0;
rapi_session->CeRapiInvoke( L"pfront_lib.dll",
L"PFRONT_BringToFront",
( ::wcslen( argv[ 1 ] ) + 1 ) * sizeof( wchar_t ),
reinterpret_cast< BYTE* >( argv[ 1 ] ),
&unused_b,
&unused_a,
NULL,
0 );
pfront_lib.dll
The RAPI extension DLL pfront_lib.dll exports only one function in the standard RAPI extension DLL form:
PFRONT_LIB_API int PFRONT_BringToFront( DWORD cbInput,
BYTE* pInput,
DWORD* pcbOutput,
BYTE** ppOutput,
IRAPIStream* pStream );
The implementation of this method is fairly simple. We will use the ToolHelper API to enumerate the list of running processes in the system. Once we find a process that matches the name given to us by the user, we will use EnumWindows
to locate any HWND
window handles owned by that process.
PFRONT_LIB_API int PFRONT_BringToFront( DWORD ,
BYTE* pInput,
DWORD* ,
BYTE** ,
IRAPIStream* )
{
const wchar_t* process_name = reinterpret_cast< const wchar_t* >( pInput );
if( NULL == process_name )
return E_INVALIDARG;
int error = ERROR_FILE_NOT_FOUND;
HANDLE snapshot =
::CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS | TH32CS_SNAPNOHEAPS, 0 );
if( INVALID_HANDLE_VALUE != snapshot )
{
PROCESSENTRY32 pe = { 0 };
pe.dwSize = sizeof( PROCESSENTRY32 );
if( ::Process32First( snapshot, &pe ) )
{
do
{
if( ::wcsicmp( pe.szExeFile, process_name ) == 0 )
{
::EnumWindows( BringToFront, pe.th32ProcessID );
error = S_OK;
break;
}
} while( ::Process32Next( snapshot, &pe ) );
}
::CloseToolhelp32Snapshot( snapshot );
}
::LocalFree( pInput );
return error;
}
If the process is found, then the BringToFront
callback is executed to determine if it owns the window. Here, we use the GetWindowThreadProcessId
function to determine if the owner of the window matches our process. If it does, SetForegroundWindow
will bring it to the front.
BOOL CALLBACK BringToFront( HWND hwnd, LPARAM lparam )
{
DWORD pid = 0;
::GetWindowThreadProcessId( hwnd, &pid );
if( pid == static_cast< DWORD >( lparam ) )
::SetForegroundWindow( hwnd );
return TRUE;
}
Conclusion
I find this tool helps my day-to-day application development and I hope that you enjoy this tool as well and that it finds a place in your toolkit with the other itsutils.