.net cf 的Remote API学习笔记(一)
2010-08-14 16:03 Aga.J 阅读(1341) 评论(0) 编辑 收藏 举报programming .net compact framework出了第2版,最近在学习RAPI,做下笔记。
RAPI是一套Win32 API,可以让桌面系统来访问链接设备的对象存储,设备和桌面电脑必须在连接的情况才能进行通信,ActiveSync或者Windows Device Center
RAPI的函数保存在C:\Windows\System32\rapi.dll
这些Win32的api作者都已经把他们封装成C#的函数了,在源码里面可以找到, 附上已经封装好的dll,YaoDurant.Win32.Rapi.rar
启动RAPI:可以使用CeRapiInit或者CeRapiInitEx(第一个是阻塞式方程)
结束:CeRapiUninit
使用CeRapiInitEx会通知caller连接已经建立,这是通过一个Win32的事件的方式来做到,我们必须将这个方法封装到net的对象里面。然后这个CeRapiInitEx方程需要传入,RAPIINIT结构作为参数,具体的结构定义和函数定义都已经在作者封装好的库里面,但是要注意RAPIINT结构有3个成员
我们必须先获得本结构的大小后,赋值给cbSize,才能作为参数传入CeRapiInitEx函数,而剩下两个参数是作为函数调用后的填充,heRepiInit保存了Win32事件(用来通知已经建立连接)的句柄,hrRepiInit则保存了初始化的结果情况。
有两种方式可以来处理RAPI的startUp
第一种是简单的单一线程RAPI Startup
namespace startUpRapi
{
class Program
{
public const int OK = 0;
static void
{
Rapi.RAPIINIT rapiInit = new Rapi.RAPIINIT(); //参数
rapiInit.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(rapiInit);//使用结构的大小来设置值
int hr = Rapi.CeRapiInitEx(ref rapiInit); //开启RAPI初始化。返回HRESULT结果
//CeRapiInit是阻塞式的启动。
//当检查到已经连接时,会通过“事件”机制来通知caller
//调用会,会填充Rapi的heRapiInit,它是win32的事件句柄,也就是通过这个句柄来告知,
//然后hrRapiInit会保存初始化结果OK或其他
System.Threading.ManualResetEvent manualResetEvent = new System.Threading.ManualResetEvent(false);
manualResetEvent.Handle = rapiInit.heRapiInit;
//接收某个事件的句柄,监听某个事件
if (manualResetEvent.WaitOne(5000, false) && rapiInit.hrRapiInit == OK)
{
Console.WriteLine("Connect established");
}
else
{
Rapi.CeRapiUninit();
Console.WriteLine("time up-no device");
}
//如果调用成功,那么就开始了RAPI连接,所有可以开始调用rapi的函数
Rapi.CeRapiUninit();
//最后需要断开连接
Console.Read();
}
}
}
第2种为Multithreaded RAPI Startup,这种方法设计到一个background thread,就是利用了这条后台运行的thread来实现RAPI的initialization。
第2种方法新建了一条线程来完成RAPI的initialization,然后通过Invoke来和外部线程的进行通信,主要是利用了外部contro和其回调方法来实现
namespace FindPrograms
{
// Table of reasons that WorkerThread calls into the
// user-interface thread.
public enum INVOKE_STARTUP
{
STARTUP_SUCCESS,
STARTUP_FAILED,
STATUS_MESSAGE
}
/// <summary>
/// StartupThread - Wrapper class that spins a thread
/// to initialize RAPI. Calls a delegate to report status.
/// </summary>
public class StartupThread
{
public string strBuffer; // Inter-thread buffer
public INVOKE_STARTUP itReason; // Inter-thread reason
private Thread m_thrd = null; // The contained thread
private Control m_ctlInvokeTarget; // Inter-thread control
private EventHandler m_deleCallback; // Inter-thread delegate
private bool m_bContinue; // Continue flag.
public bool bThreadContinue // Continue property.
{
get { return m_bContinue; }
set { m_bContinue = value; }
}
/// <summary>
/// StartupThread - Constructor.
/// </summary>
/// <param name="ctl">Owner control</param>
/// <param name="dele">Delegate to invoke</param>
public StartupThread(Control ctl, EventHandler dele) //在某个Control上启动这个background Thread来启动RAPI
{
bThreadContinue = true;
m_ctlInvokeTarget = ctl; // Who to call. //指定外部谁启动了这个线程
m_deleCallback = dele; // How to call. //指定外部回调委托,用来实现线程间的通信。
}
/// <summary>
/// Run - Init function for startup thread.
/// </summary>
/// <returns></returns>
public bool Run() //启动内部线程
{
ThreadStart ts = null;
ts = new ThreadStart(ThreadMainStartup);
if (ts == null)
return false;
m_thrd = new Thread(ts);
m_thrd.Start(); //启动内部线程
return true;
}
/// <summary>
/// ThreadMainStartup - Start RAPI connection.
/// </summary>
private void ThreadMainStartup()
{
// Allocate structure for call to CeRapiInitEx
Rapi.RAPIINIT ri = new Rapi.RAPIINIT();
ri.cbSize = Marshal.SizeOf(ri);
// Call init function
int hr = Rapi.CeRapiInitEx(ref ri);
// Wrap event handle in corresponding .NET object
ManualResetEvent mrev = new ManualResetEvent(false);
mrev.SafeWaitHandle = new SafeWaitHandle(ri.heRapiInit, true); //前面和第一种方法一样,设置必要的参数
// Wait five seconds, then fail.
if (mrev.WaitOne(5000, false) && ri.hrRapiInit == Rapi.S_OK)
{
// Notify caller that connection established.
itReason = INVOKE_STARTUP.STARTUP_SUCCESS;
m_ctlInvokeTarget.Invoke(m_deleCallback); //这里使用Invoke会通知外部控件,回调外部线程的函数。//通知外部线程,Rapi已经成功建立,由于cf中没有可带参数的Invoke,所有只能把需要的字段使用get来访问了,比如itReason。
}
else
{
// On failure, disconnect from RAPI.
Rapi.CeRapiUninit();
strBuffer = "Timeout - no device present.";
itReason = INVOKE_STARTUP.STATUS_MESSAGE; //同样进行通知
m_ctlInvokeTarget.Invoke(m_deleCallback);
// Notify caller that connection failed.
itReason = INVOKE_STARTUP.STARTUP_FAILED;
m_ctlInvokeTarget.Invoke(m_deleCallback);
}
// Trigger that thread has ended.
m_thrd = null;
}
} // class StartupThread
} // namespace FindPrograms
下面是外部线程的调用过程
private EventHandler m_deleStartup; //事件处理器
m_deleStartup = new EventHandler(this.StartupCallback);//委托给StartupCallback函数
private void StartupCallback(object sender, System.EventArgs e)
{
INVOKE_STARTUP it = this.m_thrdStartup.itReason;
switch (it)
{
case INVOKE_STARTUP.STARTUP_SUCCESS:
m_bRapiConnected = true;
EnableUI();
break;
case INVOKE_STARTUP.STARTUP_FAILED:
ResetUI();
break;
case INVOKE_STARTUP.STATUS_MESSAGE:
sbarMain.Text = m_thrdStartup.strBuffer;
break;
}
}
m_thrdStartup = new StartupThread(this, m_deleStartup); //构造函数,传入参数,“委托”
利用上面封装好的可以实现内部线程的RAPI的Startup类,我们可以很方便的进行RAPI的initialization,每条线程都在独立的类里执行,减少了冲突,而且使用Control.Invoke是线程安全的,并且本例中只有一个方向的线程使用,就是从内部线程触发到外部线程。
为什么Control.Invoke是线程安全的呢?因为它依赖的是底层Win32的SendMessage函数,这个函数会将Win32的信息发送给窗体,然后等待该消息被执行后才返回。
接下来是访问Object Store—使用RAPI函数可以访问Wince文件系统里的文件,也能访问installable file system中的文件,wince的Object store还保持registry和property database
首先是使用RAPI来访问设备上的文件
在前面那张函数介绍的图表中,我们知道我们可以在桌面系统上对我们的连接设备上的文件系统进行--创建,删除,查询目录,也可以创建删除文件,打开,读写文件。注意Wince的文件系统时Single root的目录
为了找到其他存储卡,可以使用win32的函数,FindFirstFlashCard或者FindNextFlashCard
例子中FindPrograms主要是利用StartUpRapi来启动和设备的连接,然后通过不断的调用Rapi函数CeFindFirstFile,CeFindNextFile来获得文件路径,然后就通知主线程来update它的listBox来显示这些信息,然后选择某个路径后点击run,就可以在设备上进行运行,这是通过Rapi的CeCreateProcess函数来实现的。当然这样的寻找工作也需要一条内部线程来实现,在Find的时候线程的实现和通知主线程的手段和StartupRAPI的方法一致。本例源码(书里给的源码):FindPrograms.rar
作者:Aga.J
出处:http://www.cnblogs.com/aga-j
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
个人学习笔记仅供本人记录知识所用,不属发表性文章。