CharlesChen's Technical Space

简单实用是我一直在软件开发追求的目标(I Focus on. Net technology, to make the greatest efforts to enjoy the best of life.)
Not the best, only better
  博客园  :: 首页  :: 联系 :: 订阅 订阅  :: 管理

    当用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获取子线程中异常,导致程序无法正常运行下去。相关代码如下:

代码
    public void Connect(bool WaitForInit, int TimeoutSeconds)
        {
            
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, 00"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 delegate void ExceptionEventHandler(RAPIException e);
    
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初始化时:   获取连接失败的提示,给用户展示提示信息。

 m_rapi.ThrowException += new ExceptionEventHandler(m_rapi_ThrowException);
 
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。做为学习的记录:

代码
  public partial class MainForm : Form
    {
        
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(
false2);
        }

        
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