65.9K
CodeProject 正在变化。 阅读更多。
Home

pfront:Windows Mobile 的实用工具

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (4投票s)

2011 年 5 月 2 日

CPOL

2分钟阅读

viewsIcon

17444

downloadIcon

383

Windows Mobile 流行命令行工具 itsutils 的一个小补充。

目录

引言

我是广受欢迎的 itsutils 命令行工具集合的忠实粉丝。它们允许你通过 PC 的命令提示符来控制你的已连接 Windows Mobile 设备。 本文介绍了我的一个想法,为这个工具集添加一个可以将后台应用程序窗口置于前台的功能。 我发现这比通过开始菜单进入任务管理器,在运行应用程序列表中找到我的可执行文件,然后从下拉菜单中选择“切换到”更方便。

pfront 使用方法

pfront 工具的使用方法与其他 itsutil 工具类似。 从你的 Windows PC 的命令提示符:pfront.exe application_name.exe。 如果 application_name.exe 是已经在你的 Windows Mobile 设备上运行的应用程序的名称,它的窗口将显示在前台。 如果你不知道你的应用程序是否正在运行,itsutil 命令 pps 将为你提供系统中所有进程的列表。

pfront 设计

像所有 itsutils 一样,pfront 分为两个部分:Windows 可执行文件 (pfront.exe) 和 Windows Mobile DLL (pfront_lib.dll)。

pfront.exe

pfront 可执行文件使用 RAPI2 接口将 pfront_lib.dll 库上传到移动设备,并调用其 导出的 RAPI 扩展方法。 使用 RAPI2 设备连接到已连接的 Windows Mobile 设备的代码只有短短 9 行

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();

连接后,我们使用 CeCreateFileCeWriteFilepfront_lib.dll 复制到移动设备。 在下面的代码中,我使用 boost::shared_ptr<> 来封装 CeCreateFile 返回的 HANDLE 如果你没有使用 boost,使用一个裸的 HANDLE 也没关系,就像你通常做的那样。

/// @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;
};

现在 RAPI 扩展 DLL 已上传到移动设备,只需调用 CeRapiInvoke 来调用 DLL 中导出的 RAPI 方法,并将用户在命令行参数中指定的可执行文件的名称传递给它。

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

RAPI 扩展 DLL pfront_lib.dll 仅以标准的 RAPI 扩展 DLL 形式导出一个函数

/// @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 );

该方法的实现非常简单。 我们将使用 ToolHelper API 枚举系统中正在运行的进程列表。 一旦我们找到一个与用户提供给我们的名称匹配的进程,我们将使用 EnumWindows 来查找该进程拥有的任何 HWND 窗口句柄。

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;
}

如果找到该进程,则执行 BringToFront 回调以确定它是否拥有该窗口。 在这里,我们使用 GetWindowThreadProcessId 函数来确定窗口的所有者是否与我们的进程匹配。 如果匹配,SetForegroundWindow 将将其带到前台。

/// @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;
}

结论

我发现这个工具对我的日常应用程序开发有所帮助,我希望你也能喜欢这个工具,并希望它能在你的工具包中与其他 itsutils 一起占据一席之地。

© . All rights reserved.