虽说目前.Net Micro Framework已经支持文件系统(FAT16/FAT32),但在远程还无法直接访问,从某种意义上讲,无法和PC交互的存储介质显得有些鸡肋。我做SideShow相关开发的时候,为了向该文件系统拷贝文件,实现了UsbMassStorage功能,把设备当优盘来用,但这样做,等于独占了USB口,并且设备和PC的连接也必须为USB,对仅拥有串口或网口的设备是无效的。做过WinCE或Windows Mobile开发的人都知道,VS2008开发工具提供了些远程工具,诸如远程文件查看器、远程注册表编辑器、远程堆查看器和远程放大等等。受此启发,所以才有了MF的远程文件查看器。

该远程文件查看器,仍然作为MFDeploy的插件存在(如何做插件?请参见《玩转.Net MF–01》),最终的操作主界面如下:

该功能的实现要比《让PC成为MF的鼠标键盘》还要复杂,需要修改和添加的代码较多,下面我们一一进行讲解。

实现思路:考虑到MFDeploy已经实现了读写Flash的功能,所以最初的思路是想在PC端实现FAT文件系统(我曾实现过基于硬盘的FAT16系统),但是要支持FAT16/FAT32两种模式,还是非常复杂的,并且效率也很难保证;FTP是一种远程访问文件系统的常用办法,但这是基于TCP/IP上的协议,对USB和串口并不适合,所以考虑在PC端实现一个中间层,做一个类似串口/USB转TCP的模块,这样实现的好处是FTP客户端是现成的,不用再专门开发相关操作界面,但是FTP是基于两个TCP连接,实现起来有些难度;最终还是结合FTP的特点,实现了FTP的相关指令,如PWD、CD、CDUP、MKD和DELE指令等等,美中不足的是操作界面要自己开发。

一、      信道建立

在《让PC成为MF的鼠标键盘》中,鼠标和按键信息的传递,我们是借用了MFDeploy设备访问内存的通道,虽然简单,但极不正规,有点山寨的味道。所以彻底一点,我们自己另外新建一个访问通道。

MFDeploy和设备交互(包括VS2008部署和调试设备),都是基于WireProtocol协议,该协议是上位机MFDeploy或VS2008程序在诊断、部署、调试.Net Micro Framework设备及相关应用程序时的通信协议。该协议与具体的硬件链路无关,目前支持的物理连接有串口、网口、USB等。

该协议为点对点协议,协议中没有设备地址的概念,在同一时间同一物理通道仅能调试一台设备。协议格式分两部分,帧头和负荷(Payload)(一帧命令可以不包含Payload)。详细介绍请参见我写的文章《Micro Framework WireProtocol协议介绍》。

(1)、Native Code代码

i、在TinyCLR_Debugging.h头文件CLR_DBG_Commands结构体中添加如下代码(57行),定义一个信道(我们的名称为Custom,意思是以后新扩充的基于通信交互的功能,就可以使用该信道,不仅仅是给远程文件查看器使用):

  1. static const UINT32 c_Monitor_Custom = 0x0000000F;

ii、依然在TinyCLR_Debugging.h头文件CLR_DBG_Commands结构体中添加如下代码(194行),定义我们的数据帧的通信结构(对WireProtocol协议来说就是Payload部分的数据结构):

  1. struct Monitor_Custom
  2. {
  3. UINT32 m_command;
  4. UINT32 m_length;
  5. UINT8  m_data[ 1 ];
  6. };
  7. iii、在TinyCLR_Debugging.h的CLR_DBG_Debugger结构体中添加如下项(980行),声明相关信息到来时,将执行的函数:
  8. static bool Monitor_Custom ( WP_Message* msg, void* owner );
  9. iv、为在Debugger_full.cpp文件中c_Debugger_Lookup_Request数组新添一个条目(83行),声明我们的命令:
  10. DEFINE_CMD2(Custom)
  11. v、在Debugger.cpp中添加Monitor_Custom函数的具体实现:
  12. extern bool Monitor_Custom_Process(UINT8* data,int length,UINT8* retData,int *retLength);
  13. bool CLR_DBG_Debugger::Monitor_Custom( WP_Message* msg, void* owner )
  14. {
  15. NATIVE_PROFILE_CLR_DEBUGGER();
  16. bool fRet;
  17. CLR_DBG_Debugger        * dbg = (CLR_DBG_Debugger*)owner;
  18. CLR_DBG_Commands::Monitor_Custom* cmd = (CLR_DBG_Commands::Monitor_Custom*)msg->m_payload;
  19. UINT8  retData[1024];
  20. int    retLength=0;
  21. fRet = Monitor_Custom_Process(cmd->m_data,cmd->m_length,retData,&retLength);
  22. //lcd_printf("R:%d L:%d\r\n",fRet,retLength);
  23. if(retLength==0)
  24. {
  25. dbg->m_messaging->ReplyToCommand( msg, fRet, false);
  26. }
  27. else
  28. {
  29. dbg->m_messaging->ReplyToCommand( msg, fRet, false,retData,retLength);
  30. }
  31. return fRet;
  32. }

具体的功能实现在函数Monitor_Custom_Process中实现,它如果为空,则什么事也不做(当你不需要该功能时,为了节省程序空间,你就可以这么做)。

(2)   、修改Microsoft.SPOT.Debugger.dll

i、在WireProtocol.cs文件的Commands类中添加如下代码(119行),和NativeCode的代码保持一致:

  1. public const uint c_Monitor_Custom = 0x0000000F;

ii、仍在WireProtocol.cs文件的Commands类添加Monitor_Custom类的声明(385行):

  1. public class Monitor_Custom
  2. {
  3. public uint m_command = 0;
  4. public uint m_length = 0;
  5. public byte[] m_data = null;
  6. public void PrepareForSend(uint command, byte[] data, int offset, int length)
  7. {
  8. m_command = command;
  9. m_length = (uint)length;
  10. m_data = new byte[length];
  11. Array.Copy(data, offset, m_data, 0, length);
  12. }
  13. public class Reply : IConverter
  14. {
  15. public byte[] m_data = null;
  16. public void PrepareForDeserialize(int size, byte[] data, Converter converter)
  17. {
  18. m_data = new byte[size];
  19. }
  20. }
  21. }

iii、在WireProtocol.cs文件的ResolveCommandToPayload的函数中添加如下项,声明返回数据的结构(1470行)。

  1. case c_Monitor_Custom: return new Monitor_Custom.Reply();

iv、仍是在WireProtocol.cs文件的ResolveCommandToPayload的函数中添加如下项,声明执行类(1537行)。

case c_Monitor_Custom: return new Monitor_Custom();

v、最关键的一步,消息发送接口的实现,在Engine.cs文件的Engine类中添加如下代码(2323行):

  1. public WireProtocol.IncomingMessage CustomCommand(uint command, byte[] buf, int offset, int length)
  2. {
  3. WireProtocol.Commands.Monitor_Custom cmd = new WireProtocol.Commands.Monitor_Custom();
  4. cmd.PrepareForSend(command, buf, offset, length);
  5. return SyncMessage(WireProtocol.Commands.c_Monitor_Custom, 0, cmd);

以上代码仅仅扩展了一个信道,实际上什么事也没干(路修好了,还没有车在跑)。

二、功能实现

(1)   、Native Code代码

在\DviceCode\Pal新建CustomProcess目录,我们将完成类FTP服务端的实现。

新建CustomProcess.cpp文件,文件起始先做如下声明:

  1. #define   Custom_Command_MouseKey      0x00
  2. #define   Custom_Command_FileSystem    0x01
  3. #define   FileSystem_Start             0x00
  4. #define   FileSystem_End               0x01
  5. #define   FileSystem_FORMAT            0x02
  6. #define   FileSystem_PWD               0x03
  7. #define   FileSystem_DIR_FindOpen      0x04
  8. #define   FileSystem_DIR_FindNext      0x05
  9. #define   FileSystem_DIR_FindClose     0x06
  10. #define   FileSystem_CD                0x07
  11. #define   FileSystem_CDUP              0x08
  12. #define   FileSystem_MKD               0x09
  13. #define   FileSystem_DELE              0x0A
  14. #define   FileSystem_UPLOAD            0x0B
  15. #define   FileSystem_DOWNLOAD          0x0C

Monitor_Custom_Process函数的实现如下:

  1. bool Monitor_Custom_Process(UINT8* inData,int inLength,UINT8* outData,int *outLength)
  2. {
  3. *outLength=0;
  4. if(inLength==0) return true;
  5. bool ret=true;
  6. switch(inData[0])
  7. {
  8. case Custom_Command_MouseKey:
  9. if(inLength == 9)
  10. {
  11. UINT32 data1=(inData[1]<<24) | (inData[2]<<16) |  (inData[3] <<8) | inData[4];
  12. UINT32 data2=(inData[5]<<24) | (inData[6]<<16) |  (inData[7] <<8) | inData[8];
  13. VI_GenerateEvent(data1,data2);
  14. }
  15. break;
  16. case Custom_Command_FileSystem:
  17. ret = FileSystem_Process(inData[1],&inData[2],inLength-2,outData,outLength);
  18. break;
  19. }
  20. return ret;
  21. }

你看,我们顺便也把鼠标和键盘的信道也合并到这里来了。

FileSystem_Process就是具体实现类FTP服务端功能的函数。

  1. bool  FileSystem_Process(UINT8 command,UINT8* inData,int inLength,UINT8* outData,int *outLength)
  2. {
  3. static WCHAR current_dir[256];              //当前工作目录
  4. static WCHAR current_file[256];             //当前操作文件
  5. static UINT32 findHandle=NULL;
  6. static FileSystemVolume* volume = NULL;
  7. static STREAM_DRIVER_INTERFACE*  streamDriver=NULL;
  8. //debug_printf("[%x:%d]\r\n",command,inLength);
  9. if( volume == NULL && command != FileSystem_Start) return false;
  10. HRESULT ret=S_OK;
  11. *outLength=0;
  12. switch(command)
  13. {
  14. case  FileSystem_Start:
  15. volume = FileSystemVolumeList::GetFirstVolume();
  16. streamDriver = volume->m_streamDriver;
  17. memcpy(current_dir, L"\\", 2);
  18. current_dir[1]=0;
  19. break;
  20. case  FileSystem_End:
  21. volume->FlushAll();
  22. volume = NULL;
  23. streamDriver = NULL;
  24. break;
  25. case  FileSystem_FORMAT:
  26. volume->Format(0);
  27. volume->FlushAll();
  28. break;
  29. case  FileSystem_PWD:
  30. *outLength = MF_wcslen(current_dir)*2;
  31. memcpy(outData,current_dir, *outLength);
  32. break;
  33. case  FileSystem_DIR_FindOpen:
  34. case  FileSystem_DIR_FindNext:
  35. case  FileSystem_DIR_FindClose:
  36. case  FileSystem_CDUP:
  37. case  FileSystem_MKD:
  38. case  FileSystem_DELE:
  39. case  FileSystem_UPLOAD:
  40. case  FileSystem_DOWNLOAD:
  41. //略
  42. break;
  43. }
  44. return true;
  45. }

在TinyCLR.proj文件中添加如下项,以启用我们新实现的功能。

  1. <ItemGroup>
  2. <RequiredProjects Include="$(SPOCLIENT)\DeviceCode\PAL\CustomProcess\dotNetMF.proj" />
  3. <DriverLibs Include="CustomProcess.$(LIB_EXT)" />
  4. </ItemGroup>

编译下载,这时候,我们的设备已经可支持文件系统远程访问了。下一步我们将实现上位机的相关代码。

(2)、远程文件查看器插件

新建FileViewerHandle插件类。

public class FileViewerHandle : MFPlugInMenuItem

{

public override string Name { get { return "File Viewer"; } }

public override void OnAction(IMFDeployForm form, MFDevice device)

{

if (form == null || device == null) return;

(new frmFileViewer(form, device)).ShowDialog();

}

}

核心函数Send的代码如下,该函数与设备进行通信:

private bool Send(byte command, byte[] bytInput, out byte[] bytOutput)

{

byte[] bytTemp;

bytOutput = null;

if (bytInput != null)

{

bytTemp = new byte[bytInput.Length + 2];

Array.Copy(bytInput, 0, bytTemp, 2, bytInput.Length);

}

else

{

bytTemp = new byte[2];

}

bytTemp[0] = Custom_Command_FileSystem;

bytTemp[1] = command;

_WP.IncomingMessage reply = engine.CustomCommand(_WP.Commands.c_Monitor_Custom, bytTemp, 0, bytTemp.Length);

if (!_WP.IncomingMessage.IsPositiveAcknowledge(reply)) return false;

_WP.Commands.Monitor_Custom.Reply CustomReply = reply.Payload as _WP.Commands.Monitor_Custom.Reply;

if (CustomReply != null) bytOutput = CustomReply.m_data;

return true;

}

限于篇幅,下面仅截取几个命令操作的片段

private void tsbUP_Click(object sender, EventArgs e)

{

ShowInfo("ready.");

byte[] outData;

if (Send(FileSystem_CDUP, out outData))

{

txtDir.Text = System.Text.Encoding.Unicode.GetString(outData);

}

else

{

ShowInfo("up failed!", InfoType.Error);

}

list();

}

private void tsbNew_Click(object sender, EventArgs e)

{

ShowInfo("ready.");

frmInputBox fb = new frmInputBox("Input directory", "");

if (fb.ShowDialog() == DialogResult.OK)

{

string strInfo = fb.Info;

byte[] inData = System.Text.Encoding.Unicode.GetBytes(BuildPath(strInfo));

if (!Send(FileSystem_MKD, inData)) ShowInfo("mkd failed!", InfoType.Error);

list();

}

}

private void tsbDelete_Click(object sender, EventArgs e)

{

ShowInfo("ready.");

if (lvFile.SelectedItems.Count == 0) return;

if (MessageBox.Show("Really implement operation for Delete?", this.Text, MessageBoxButtons.OKCancel,MessageBoxIcon.Question) == DialogResult.OK)

{

byte[] inData = System.Text.Encoding.Unicode.GetBytes(BuildPath(lvFile.SelectedItems[0].Text));

if (!Send(FileSystem_DELE, inData)) ShowInfo("dele failed!", InfoType.Error);

lstFile.Remove(lvFile.SelectedItems[0].Text);

list();

}

}

实现.Net MF的远程文件查看器意义深远,我们不仅可以向文件系统拷贝我们的图片、字体等资源,更有意义的事,我们可以把.Net MF应用的程序,作为可执行文件拷贝到文件系统,然后让TinyCLR去加载执行。甚而.Net MF的系统库也可以放入文件系统,根据需要,我们可以非源码级别的裁剪系统的大小。

【玩转.Net MF – 03】远程文件查看器相关推荐

  1. 试试Visual Studio中新的远程文件查看器

    今天,我们很高兴地宣布,在 Visual Studio 17.6 Preview 1 中,一项新的工具窗口诞生:远程文件查看器. 通过这个工具窗口,你可以从远程计算机上浏览,上传和下载文件.另外,可以 ...

  2. 使用广泛的开源PCB文件查看器 Gerbv 含多个严重漏洞

     聚焦源代码安全,网罗国内外最新资讯! 编译:代码卫士 专栏·供应链安全 数字化时代,软件无处不在.软件如同社会中的"虚拟人",已经成为支撑社会正常运转的最基本元素之一,软件的安全 ...

  3. WPF 模仿 UltraEdit 文件查看器系列 开篇和导读

    WPF 模仿 UltraEdit 文件查看器系列 开篇和导读 运行环境:Win10 x64, NetFrameWork 4.8, 作者:乌龙哈里,日期:2019-05-10 学 .Net FrameW ...

  4. Linux 下高级日志文件查看器Log File Navigator

    Log File Navigator,简称lnav,是一款面向小规模的适用于 Linux 的高级日志文件查看器.它是一个终端应用程序,可以理解您的日志文件,让您轻松找到问题,几乎不需要什么设置. ln ...

  5. bin文件查看器app_腾讯文件:腾讯官方出品的微信 / QQ 文件管理和清理利器

    前言 微信和 QQ 基本是大家手机上的必备应用了,但是使用时间久了,难免会积攒很多文件,甚至引起应用卡顿. 包括垃圾/缓存文件,或者我们接收保存的文件等等,占据了大量空间,又不好管理. 今天就分享一款 ...

  6. 【实用软件】Json文件查看器(支持查看超大JSON文件)

    内容信息 软件类型:绿色 软件平台:电脑 软件版本:v1.0 软件大小:3.4MB 软件特点 Json文件查看器是一个用来查看Json文件的的绿色软件

  7. 好用的Bin文件查看器,J-flash

    工作中,很多地方用到Bin文件,如编译完成后的固件和从MCU的Flash读出来的文件,这时候一个好的Bin文件查看器至关重要.经常我们用STM32自带的STM32 ST-LINK Utility可以直 ...

  8. IFC模型文件查看器(基于IFC++开源库实现)

    关于IFC IFC是由buildingSMART以工业的产品资料交换标准STEP编号ISO-10303-11的产品模型信息描述用EXPERSS语言为基础,基于BIM中AEC/FM相关领域信息交流所指定 ...

  9. html文件阅读器电脑版,Excel文件查看器

    Excel文件查看器电脑版是一款文档查看软件,Excel文件查看器电脑版对于需要临时查看Excel文档但是又不需要修改它的用户来说,Excel文件查看器电脑版无疑是最佳选择,速度快并且小巧,是个不错的 ...

最新文章

  1. linux cmake编译源码,linux安装mysql(源码)以及cmake编译
  2. IP头结构&其他解析
  3. SQL中的重复记录操作
  4. memmove 对同一个指针不操作
  5. WebService初入
  6. 微信生态下的营销洞察
  7. Linux OpenSSL获取证书指纹值(443、MD5、SHA1、SHA256)
  8. 知识图谱的概念、应用与构建
  9. UI设计动效\动画素材模板|分层分步骤学习动效设计
  10. day80 django模版学习
  11. Vue第三天 v-model与Vue组件化
  12. MongoDb系列文章
  13. 智能告警——企业IT系统神经中枢
  14. 魔兽争霸3冰封王座,打不开,提示“此版本之魔兽争霸3需要特定语言版本之windows“
  15. 手机支付宝服务器安全证书安装不了,手机上如何安装支付宝的安全证书?
  16. 【ARM】Kylin V10/ARM平台上源码安装gcc 11.2
  17. c语言 实现推箱子小游戏
  18. linux中安装openoffice
  19. 【数据挖掘】K-Means 一维数据聚类分析示例
  20. pdf转换器免费版下载

热门文章

  1. 【java】java反射机制,动态获取对象的属性和对应的参数值,并属性按照字典序排序,Field.setAccessible()方法的说明【可用于微信支付 签名生成】...
  2. 转载:Ununtu下中文乱码解决方案
  3. ESXI上的vm虚拟机文件被锁定无法POWER ON的问题处理
  4. 为centos选择国内yum软件库
  5. AS3与lua之间的交互
  6. 安装SQL Server 2012示例数据库
  7. 【 58沈剑 架构师之路】各种SQL到底加了什么锁?
  8. 微软 Surface Pro、Studio、Laptop 全线更新
  9. 数字签名、数字证书、对称加密算法、非对称加密算法、单向加密(散列算法)...
  10. Android中的ViewDragHelper