pfront:Windows Mobile 的实用工具
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();
连接后,我们使用 CeCreateFile
和 CeWriteFile
将 pfront_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 一起占据一席之地。