zoukankan      html  css  js  c++  java
  • 【玩转.Net MF – 03】远程文件查看器

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

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

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

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

    一、      信道建立

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

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

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

    1)、Native Code代码

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

     static const UINT32 c_Monitor_Custom = 0x0000000F;

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

        struct Monitor_Custom

        {

            UINT32 m_command;

            UINT32 m_length;

            UINT8 m_data[ 1 ];

        };

    iiiTinyCLR_Debugging.hCLR_DBG_Debugger结构体中添加如下项(980行),声明相关信息到来时,将执行的函数:

    static bool Monitor_Custom ( WP_Message* msg, void* owner );

    iv为在Debugger_full.cpp文件中c_Debugger_Lookup_Request数组新添一个条目(83行),声明我们的命令:

    DEFINE_CMD2(Custom)

    vDebugger.cpp中添加Monitor_Custom函数的具体实现:

    extern bool Monitor_Custom_Process(UINT8* data,int length,UINT8* retData,int *retLength);

    bool CLR_DBG_Debugger::Monitor_Custom( WP_Message* msg, void* owner )

    {

        NATIVE_PROFILE_CLR_DEBUGGER();

        bool fRet;

     

        CLR_DBG_Debugger        * dbg = (CLR_DBG_Debugger*)owner;

        CLR_DBG_Commands::Monitor_Custom* cmd = (CLR_DBG_Commands::Monitor_Custom*)msg->m_payload;

     

         UINT8 retData[1024];

         int    retLength=0;

        fRet = Monitor_Custom_Process(cmd->m_data,cmd->m_length,retData,&retLength);  

     

        //lcd_printf("R:%d L:%d\r\n",fRet,retLength);

         if(retLength==0)

         {

           dbg->m_messaging->ReplyToCommand( msg, fRet, false);

         }

         else

         {

            dbg->m_messaging->ReplyToCommand( msg, fRet, false,retData,retLength);

         }

        return fRet;

    }

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

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

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

    public const uint c_Monitor_Custom = 0x0000000F;

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

    public class Monitor_Custom

            {

                public uint m_command = 0;

                public uint m_length = 0;

                public byte[] m_data = null;

     

                public void PrepareForSend(uint command, byte[] data, int offset, int length)

                {

                    m_command = command;

                    m_length = (uint)length;

                    m_data = new byte[length];     

                    Array.Copy(data, offset, m_data, 0, length);

                }

                public class Reply : IConverter

                {

                    public byte[] m_data = null;

                    public void PrepareForDeserialize(int size, byte[] data, Converter converter)

                    {

                        m_data = new byte[size];

                    }

                }

          }

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

    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行):

          public WireProtocol.IncomingMessage CustomCommand(uint command, byte[] buf, int offset, int length)

            {

                WireProtocol.Commands.Monitor_Custom cmd = new WireProtocol.Commands.Monitor_Custom();

                cmd.PrepareForSend(command, buf, offset, length);  

                return SyncMessage(WireProtocol.Commands.c_Monitor_Custom, 0, cmd);

     }

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

    二、功能实现

    (1)   Native Code代码

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

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

         #define   Custom_Command_MouseKey      0x00

         #define   Custom_Command_FileSystem    0x01

         #define   FileSystem_Start             0x00

         #define   FileSystem_End               0x01

         #define   FileSystem_FORMAT            0x02

         #define   FileSystem_PWD               0x03

         #define   FileSystem_DIR_FindOpen      0x04

         #define   FileSystem_DIR_FindNext      0x05

         #define   FileSystem_DIR_FindClose     0x06

         #define   FileSystem_CD                0x07

         #define   FileSystem_CDUP              0x08

         #define   FileSystem_MKD               0x09

         #define   FileSystem_DELE              0x0A

         #define   FileSystem_UPLOAD            0x0B

         #define   FileSystem_DOWNLOAD          0x0C

         Monitor_Custom_Process函数的实现如下:

         bool Monitor_Custom_Process(UINT8* inData,int inLength,UINT8* outData,int *outLength)

    {  

        *outLength=0;

        if(inLength==0) return true;

          bool ret=true;

        switch(inData[0])

        {

           case Custom_Command_MouseKey:   

              if(inLength == 9)

               {

                 UINT32 data1=(inData[1]<<24) | (inData[2]<<16) | (inData[3] <<8) | inData[4];

                    UINT32 data2=(inData[5]<<24) | (inData[6]<<16) | (inData[7] <<8) | inData[8];           

                 VI_GenerateEvent(data1,data2);

               }

              break;

             case Custom_Command_FileSystem:

               ret = FileSystem_Process(inData[1],&inData[2],inLength-2,outData,outLength);

               break;

        }

       

        return ret;

    }

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

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

    bool FileSystem_Process(UINT8 command,UINT8* inData,int inLength,UINT8* outData,int *outLength)

    {  

       static WCHAR current_dir[256];              //当前工作目录  

        static WCHAR current_file[256];             //当前操作文件

        static UINT32 findHandle=NULL;

     

        static FileSystemVolume* volume = NULL;

        static STREAM_DRIVER_INTERFACE* streamDriver=NULL;

     

       //debug_printf("[%x:%d]\r\n",command,inLength);

       if( volume == NULL && command != FileSystem_Start) return false;

     

        HRESULT ret=S_OK;

       *outLength=0;

        switch(command)

        {

           case FileSystem_Start:

            volume = FileSystemVolumeList::GetFirstVolume();  

              streamDriver = volume->m_streamDriver;

             memcpy(current_dir, L"\\", 2);

             current_dir[1]=0;

            break;           

           case FileSystem_End:

            volume->FlushAll();

             volume = NULL;

             streamDriver = NULL;

            break;           

           case FileSystem_FORMAT:

            volume->Format(0);

             volume->FlushAll();

            break;           

           case FileSystem_PWD:

             *outLength = MF_wcslen(current_dir)*2;

              memcpy(outData,current_dir, *outLength);

            break;           

            case FileSystem_DIR_FindOpen:     

            case FileSystem_DIR_FindNext: 

          case FileSystem_DIR_FindClose:

            case FileSystem_CDUP:

    case FileSystem_MKD:     

    case FileSystem_DELE:         

    case FileSystem_UPLOAD:

            case FileSystem_DOWNLOAD:

            //

            break;           

        }

        return true;

    }

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

     <ItemGroup>

        <RequiredProjects Include="$(SPOCLIENT)\DeviceCode\PAL\CustomProcess\dotNetMF.proj" />

        <DriverLibs Include="CustomProcess.$(LIB_EXT)" />

     </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的系统库也可以放入文件系统,根据需要,我们可以非源码级别的裁剪系统的大小。
  • 相关阅读:
    CentOS VPS
    Make 命令教程
    Nginx 学习
    字体对齐问题
    postman curl
    linux命令
    服务器
    操作系统
    nginx-1.15遇到无法加载php文件报404的问题
    thinkphp6.0框架下载与安装
  • 原文地址:https://www.cnblogs.com/yefanqiu/p/1673373.html
Copyright © 2011-2022 走看看