Click here to Skip to main content
Click here to Skip to main content

pfront: A utility for Windows Mobile

, 2 May 2011
Rate this:
Please Sign up or sign in to vote.
A small addition to the popular command-line itsutils for Windows Mobile.

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.

/// @brief Copy a file to the attached device if it does not already exist
/// @param session - open RAPI2 session
/// @param local_file - path to the local file
/// @param remote_file - path to the remote file
/// @return BOOL - TRUE on success
static BOOL CopyToDevice( IRAPISession* session, 
                          const wchar_t* local_src_file, 
                          const wchar_t* remote_dest_file )
{
    BOOL success = FALSE;

    // open the local file first
    std::ifstream local( local_src_file, std::ios_base::binary );
    if( local.is_open() )
    {
        // create the remote file if it doesn't already exist
        // must have BOOST_MEM_FN_ENABLE_STDCALL defined
        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;
            
            // copy the local file to the dev
            while( local.good() )
            {
                local.read( buffer, sizeof( buffer ) );
                if( !session->CeWriteFile( remote_file.get(), 
                                           buffer, 
                                           local.gcount(), 
                                           &bytes_written, 
                                           NULL ) ) 
                {
                    break;
                }

                // we succeed if we successfully copy the entire file
                success = local.eof();
            }
        }
        else
        {
            // check to see if we failed because the file is already on the dev
            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:

/// @brief  bring the application window of the app with the given name to 
/// the front of the window z-order.
///
/// @param  DWORD cbInput - number of bytes in the pInput buffer.
/// @param  BYTE* pInput - [in] wchar_t* - null-terminated name of the 
/// executable with the window to bring to the front. e.g. "myapp.exe"
/// @param  DWORD* pcbOutput - unused.
/// @param  BYTE** ppOutput - unused.
/// @param  IRAPIStream* pStream - unused. NULL, always.
/// @return int - S_OK on success.
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 /*cbInput*/, 
                                        BYTE* pInput,
                                        DWORD* /*pcbOutput*/, 
                                        BYTE** /*ppOutput*/,
                                        IRAPIStream* /*pStream*/ )
{
    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 )
                {
                    // we've located a process that has the given executable
                    // name. Find its window and send it to the foreground.
                    ::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.

/// @brief If the process that owns the given window handle matches the PID, 
/// bring that window to the front of the z-order.
/// @param[in] hwnd window handle
/// @param[in] lparam PID of the window owner process
/// @return TRUE, always
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Paul Heil
Software Developer (Senior) An engineering firm in Cedar Rapids, Iowa
United States United States
I'm also on the MSDN forums
http://social.msdn.microsoft.com/profile/paulh79

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 2 May 2011
Article Copyright 2011 by Paul Heil
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid