当用PC机读取和操作PDA时候,需要用到RAPI,那么为了更好的使用RAPI,我们会使用到OpenNETCF.Desktop.Communication.dll文件,这个文件在Rapi.dll(win32 api)的基础上进行了NET封装。我们更能方便的使用它。对于连接和断开移动设备的方法,在使用过程中出现了一点问题,这里将记录下来,和朋友们分享:
1、当我们首先实例化时:RAPI m_rapi = new RAPI();调用:m_rapi.Connect(false, 2).如果当PDA和PC没有连接成功时候,根据OpenNetCF.Desktop.Communication里面的方法,就会在子线程中抛出异常,主线程不能Catch获取子线程中异常,导致程序无法正常运行下去。相关代码如下:
{
int ret = 0 ;
m_timeout = TimeoutSeconds;
if (WaitForInit)
{
ret = CeRapiInit();
if ( ret != 0 )
{
int e = CeRapiGetError();
Marshal.ThrowExceptionForHR(ret);
}
lock ( this )
{
m_connected = true ;
}
// throw the connected event
if (RAPIConnected != null )
{
RAPIConnected();
}
return ;
}
// non-blocking init call
m_ri = new RAPIINIT();
m_ri.cbSize = Marshal.SizeOf(m_ri);
m_ri.hrRapiInit = m_InitResult;
m_hInitEvent = CreateEvent(IntPtr.Zero, 0 , 0 , " OpenNETCF.RAPI " );
if (( uint )m_hInitEvent == uint .MaxValue)
{
throw new RAPIException( " Failed to Initialize RAPI " );
}
m_ri.heRapiInit = m_hInitEvent;
ret = CeRapiInitEx( ref m_ri);
if (ret != 0 )
{
Marshal.ThrowExceptionForHR(ret);
}
// create a wait thread
m_initThread = new Thread( new ThreadStart(InitThreadProc));
// Start thread
m_initThread.Start();
}
private void InitThreadProc()
{
int ret = 0 ;
int timeout = m_timeout * 4 ;
bool infinitetimeout = (timeout < 0 );
// check for Init event 4 times / sec
do
{
// check for abort command from Dispose()
if (m_killThread)
{
return ;
}
// see if the event is set
ret = WaitForSingleObject(m_ri.heRapiInit, 250 );
if ((ret == WAIT_FAILED) || (ret == WAIT_ABANDONED))
{
throw new RAPIException( " Failed to Initialize RAPI " );
}
if ( ! infinitetimeout)
{
if (timeout -- < 0 )
{
throw new RAPIException( " Timeout waiting for device connection " );
}
}
} while (ret != WAIT_OBJECT_0);
// check the hresult
if (m_InitResult != 0 )
{
Marshal.ThrowExceptionForHR(m_InitResult);
}
lock ( this )
{
m_connected = true ;
}
// throw the connected event
if (RAPIConnected != null )
{
RAPIConnected();
}
// clean up
CloseHandle(m_hInitEvent);
}
上面的代码当我们调用m_rapi.Connect(false,2)时候,最终在子线程中抛出异常:Timeout waiting for device connection
为了在主线程中捕捉在子线程中出现的异常,我们对上面的代码做一些修改:
public event ExceptionEventHandler ThrowException;
protected virtual void OnThrowException(RAPIException e)
{
if (ThrowException != null )
{
ThrowException(e);
}
}
private void InitThreadProc()
{
int ret = 0 ;
int timeout = m_timeout * 4 ;
bool infinitetimeout = (timeout < 0 );
// check for Init event 4 times / sec
do
{
// check for abort command from Dispose()
if (m_killThread)
{
return ;
}
// see if the event is set
ret = WaitForSingleObject(m_ri.heRapiInit, 250 );
if ((ret == WAIT_FAILED) || (ret == WAIT_ABANDONED))
{
CeRapiUninit();
OnThrowException( new RAPIException( " Failed to Initialize RAPI " ));
return ;
if ( ! infinitetimeout)
{
if (timeout -- < 0 )
{
CeRapiUninit();
OnThrowException( new RAPIException( " Timeout waiting for device connection " ));
return ;
}
}
} while (ret != WAIT_OBJECT_0);
// check the hresult
if (m_InitResult != 0 )
{
Marshal.ThrowExceptionForHR(m_InitResult);
}
lock ( this )
{
m_connected = true ;
}
// throw the connected event
if (RAPIConnected != null )
{
RAPIConnected();
}
// clean up
CloseHandle(m_hInitEvent);
}
通过对上面代码的修改,我们定义了ExceptionEventHandler委托,那么当m_rapi初始化时: 获取连接失败的提示,给用户展示提示信息。
void m_rapi_ThrowException(RAPIException e)
{
MessageBox.Show( " 连接超时!请确认把PDA和PC机连接好! " );
}
上面的修改的代码中添加了CeRapiUninit()这个方法的调用,也就是说当PC和PDA没有连接时候,调用CeRapiInitEx方法,连接超时,但是资源仍然被占用,因此需要调用这个方法来释放资源。
[DllImport("rapi.dll", CharSet = CharSet.Unicode)]
internal static extern int CeRapiUninit();
曾经在项目中因没有调用这个CeRapiUninit时候,在pda和pc机没有连接:设备断开,抛出上面的异常情况给用户提示后,再将pda和pc连接,发现当pda和pc连接不成功,再一次断开,再一次连接,断开,始终都是连接不成功。最后发现问题是第一次连接设备抛出异常后没有释放资源引起的。
2、为了详细说明关于pda和pc连接状况监控,自己写了一个简单demo。做为学习的记录:
{
internal class MyEventArgs : EventArgs
{
public Control Target;
public string Text;
public MyEventArgs(Control target, string text)
{
Target = target;
Text = text;
}
}
private RAPI m_rapi = new RAPI();
private EventHandler statusUpdate;
public MainForm()
{
InitializeComponent();
m_rapi.RAPIConnected += new RAPIConnectedHandler(m_rapi_RAPIConnected);
m_rapi.RAPIDisconnected += new RAPIConnectedHandler(m_rapi_RAPIDisconnected);
m_rapi.ActiveSync.Listen += new ListenHandler(ActiveSync_Listen);
m_rapi.ActiveSync.Answer += new AnswerHandler(ActiveSync_Answer);
m_rapi.ThrowException += new ExceptionEventHandler(m_rapi_ThrowException);
statusUpdate = new EventHandler(StatusUpdate);
m_rapi.Connect( false , 2 );
}
private void StatusUpdate( object sender, EventArgs args)
{
(sender as Label).Text = (args as MyEventArgs).Text;
}
void m_rapi_ThrowException(RAPIException e)
{
MessageBox.Show( " 连接超时!请确认把PDA和PC机连接好! " );
}
void ActiveSync_Answer()
{
if ( this .label1.InvokeRequired)
{
this .Invoke(statusUpdate, new object [] { this .label1, new MyEventArgs( this .label1, " 连接状态:连接中...! " ) });
}
else
{
statusUpdate( this .label1, new MyEventArgs( this .label1, " 连接状态:连接中...! " ));
}
}
void ActiveSync_Listen()
{
if ( this .label1.InvokeRequired)
{
this .Invoke(statusUpdate, new object [] { this .label1, new MyEventArgs( this .label1, " 连接状态:设备未连接! " ) });
}
else
{
statusUpdate( this .label1, new MyEventArgs( this .label1, " 连接状态:设备未连接! " ));
}
}
void m_rapi_RAPIDisconnected()
{
if ( this .label1.InvokeRequired)
{
this .Invoke(statusUpdate, new object [] { this .label1, new MyEventArgs( this .label1, " 连接状态:连接关闭! " ) });
}
else
{
statusUpdate( this .label1, new MyEventArgs( this .label1, " 连接状态:连接关闭! " ));
}
}
void m_rapi_RAPIConnected()
{
if ( this .label1.InvokeRequired)
{
this .Invoke(statusUpdate, new object [] { this .label1, new MyEventArgs( this .label1, " 连接状态:设备已连接! " ) });
}
else
{
statusUpdate( this .label1, new MyEventArgs( this .label1, " 连接状态:设备已连接! " ));
}
}
private void MainForm_Load( object sender, EventArgs e)
{
}
private void button1_Click( object sender, EventArgs e)
{
if (m_rapi.DeviceFileExists( @" \***\****.sdf " ))
{
MessageBox.Show( " 文件存在!连接是成功的! " );
}
else
{
MessageBox.Show( " 文件不存在!,连接不成功! " );
}
}
}
通过demo发现,连接状态的变化过程:
1.连接pda时候:设备未连接---->连接中...----->设备已连接
2.断开pda上:设备已连接----->连接关闭------->设备未连接
Best Regards,
Charles Chen