程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 【玩轉.Net MF – 02】讓PC成為MF的鼠標鍵盤

【玩轉.Net MF – 02】讓PC成為MF的鼠標鍵盤

編輯:關於.NET

考慮一個應用場景,你設計了一個多功能帶LCD顯示的儀器,假設為了節省成 本,沒有安裝觸摸屏和擴展外接鼠標鍵盤的接口,儀表上僅有幾個外置按鈕,但 是由於功能相對復雜,需要配置很多參數,如果單單依靠外置按鈕,輸入不僅慢 ,還得為此設計一套輸入規則,想想看如果能通過儀表調試口,通過擴展讓我們 的PC成為它的鼠標鍵盤,則輸入工作將變的異常簡單(其實這樣的儀表並不是我 憑空瞎想,以前開發ICU輸液系統時,國外生產的輸液裝置就是這樣的儀表,比如 要輸入藥名、輸液速度和輸液壓力等一系列相關參數)。

通過擴展我以前為.Net MF開發的WinForm庫(參見我以前的文章《開源 System.Windows.Forms庫,讓.Net Micro Framework界面開發和上位機一樣簡單 》),增加一個輸入代理層,就可以實現虛擬鼠標和鍵盤輸入。

先看看最終的成果(如下圖),然後我們再細說是如何實現的。

(虛擬鼠標輸入,設備上的鼠標將和PC上的鼠標同步移動)

(虛擬鍵盤輸入,千萬不要以為上面的字符為軟鍵盤所輸入,細心的讀者會發 現,軟鍵盤上根本沒有 @#¥%等按鍵)

要實現這個功能還真不那麼簡單,需要做如下四步工作:一、為MFDeploy開發 一個插件,捕捉PC上的鼠標和按鍵信息,並把它們發送到設備;二、修改TinyCLR 內核代碼,讓它獲取PC上發送的鼠標和按鍵信息;三、為.Net MF添加一個事件源 ,當有鼠標和按鍵時,觸發.Net MF應用程序中特定的事件;四、為WinForm庫擴 展一個輸入代理層。下面我們將一一介紹上面四步的實現步驟。

一、MFDeploy虛擬輸入插件

在上一篇同系列的文章《【玩轉.Net MF – 01】Flash遠程讀寫》,我們已經 介紹了MFDeploy的實現方法,所以這裡就不熬述了。

實現思路的是這樣的,因為我們已經通過MFDeploy可以和設備進行通信,在不 額外增加代碼的情況下,我們虛擬一個寫Flash 的操作,通過該渠道,把鼠標和 按鍵信息發送給設備。實現代碼如下:

private void SendKey(KeyState state, Keys KeyCode, int  KeyValue, bool Shift, bool Caps, bool Ins)
         {
             byte[] bytData = new byte[8]{ 0x01, (byte)state,(byte)KeyCode, (byte)KeyValue, (byte)(Shift ? 1 : 0),  (byte)(Caps ? 1 : 0), (byte)(Ins ? 1 : 0),0};
             SendData(bytData);
         }
         private void SendMouse(MouseState state,  MouseButtons button, int x, int y)
         {
             byte[] bytData = new byte[8] { 0x02,  (byte)state, (byte)(button == MouseButtons.Left ? 1 : 0),  (byte)(button == MouseButtons.Right ? 1 : 0), (byte)(x >>  8), (byte)(x & 0xFF), (byte)(y >> 8), (byte)(y  & 0xFF) };
             SendData(bytData);
         }
         private void SendData(byte[] bytData)
         {
             if (bytData.Length != 8) return;
             UInt32 addr = 0xD;
             byte[] TempData = new byte[10] {0xAA,  0x55,0,0,0,0,0,0,0,0};
             Array.Copy(bytData, 0, TempData, 2,  8);
             bool ret = engine.WriteMemory(addr,  TempData, 0, 10);
         }
         private void palScreen_MouseDown(object sender,  MouseEventArgs e)
         {
             SendMouse(MouseState.Down, e.Button, e.X,  e.Y);
         }
         private void palScreen_MouseMove(object sender,  MouseEventArgs e)
         {
             SendMouse(MouseState.Move, e.Button, e.X,  e.Y);
         }
         private void palScreen_MouseUp(object sender,  MouseEventArgs e)
         {
             SendMouse(MouseState.Up, e.Button, e.X,  e.Y);
         }
         private void frmVirtualInput_KeyDown(object  sender, KeyEventArgs e)
         {
             lblKey.Text = e.KeyCode.ToString();
             SendKey(KeyState.Down, e.KeyCode, (int) e.KeyValue, e.Shift, false, false);
         }
         private void frmVirtualInput_KeyUp(object sender,  KeyEventArgs e)
         {
             SendKey(KeyState.Up, e.KeyCode, (int) e.KeyValue, e.Shift, false, false);
         }
         private void frmVirtualInput_KeyPress(object  sender, KeyPressEventArgs e)
         {
             byte[] bytChar =  System.Text.ASCIIEncoding.UTF8.GetBytes(e.KeyChar.ToString());
             SendKey(KeyState.Press, Keys.Space, (int) bytChar[0], false, false, false);
    }

窗體的大小通過獲取設備的LCD的尺寸,進行自動設置,這等於虛擬出一個與 LCD等大的鼠標活動區。

二、鼠標和按鍵信息獲取

上一步我們向設備寫入了一個虛擬寫Flash操作,所以我們在設備的上的代碼 ,需做些修改,還是修改\CLR\Debugger目錄下的Debugger.cpp文件,在 CLR_DBG_Debugger::AccessMemory函數增添如下代碼:

case AccessMemory_Write:
      if(accessAddress == 0xD && NumOfBytes==10  && bufPtr[0] == 0xAA && bufPtr[1] == 0x55 )
     {
          UINT32 data1=(bufPtr[2]<<24) | (bufPtr [3]<<16) | (bufPtr[4] <<8) | bufPtr[5];
          UINT32 data2=(bufPtr[6]<<24) | (bufPtr [7]<<16) | (bufPtr[8] <<8) | bufPtr[9];
          VI_GenerateEvent(data1,data2);
          success = TRUE;
     }
     else
     {
          success = m_deploymentStorageDevice->Write(  accessAddress , NumOfBytes, (BYTE *)bufPtr, FALSE );
         }
     break;

記得在函數頭聲明VI_GenerateEvent函數,它負責觸發消息。

extern void VI_GenerateEvent(UINT32 data1, UINT32 data2);

三、實現事件觸發

我們在\DeviceCode\pal目錄新建一個VirtualInput子目錄,我們要為TinyCLR 新添加一個feature。核心代碼如下:

void VI_GenerateEvent(UINT32 data1, UINT32 data2)
{
     if(g_Context)
     {
         GLOBAL_LOCK(irq);
         SaveNativeEventToHALQueue( g_Context,data1,  data2);
     }
}
static HRESULT InitializeEventDriver(  CLR_RT_HeapBlock_NativeEventDispatcher *pContext, UINT64 userData  )
{
    g_Context = pContext;
    g_UserData = userData;
    return S_OK;
}
static HRESULT EnableDisableEventDriver(  CLR_RT_HeapBlock_NativeEventDispatcher *pContext, bool fEnable )
{
    g_InterruptEnalbed = fEnable;
    return S_OK;
}
static HRESULT CleanupIestDriver(  CLR_RT_HeapBlock_NativeEventDispatcher *pContext )
{
     g_Context = NULL;
     g_UserData = 0;
     CleanupNativeEventsFromHALQueue( pContext );
     return S_OK;
}
static const CLR_RT_DriverInterruptMethods  g_InteropEventDriverMethods =
{
     InitializeEventDriver,
     EnableDisableEventDriver,
     CleanupIestDriver
};
const CLR_RT_NativeAssemblyData  g_CLR_AssemblyNative_Microsoft_SPOT_YFVI_Event =
{
     "Microsoft_SPOT_YFVI_Event",
     DRIVER_INTERRUPT_METHODS_CHECKSUM,
     &g_InteropEventDriverMethods
};

以上代碼就是.Net MF實現事件觸發的典型結構,比如按鍵按下抬起、串口數 據接收、SD卡插入等等事件通知就是如此實現的。

四、WinForm輸入代理實現

接收事件的代碼如下,也是一個標准結構。

public class EventDispatcher : NativeEventDispatcher
     {
         public EventDispatcher()
             : base("Microsoft_SPOT_YFVI_Event", 0)
         { }
         public EventDispatcher(string EventDispatcherName,  ulong userData)
             : base(EventDispatcherName, userData)
         { }
}

"Microsoft_SPOT_YFVI_Event"要和上一步 g_CLR_AssemblyNative_Microsoft_SPOT_YFVI_Event中的名字保持一致。

為WinForm庫新添加一個YFSoft.InputProxy.dll庫,和虛擬輸入相關的代碼如 下:

private void eventDispatcher_OnInterrupt(uint data1, uint  data2, DateTime time)
         {
             byte[] bytData = new byte[8];
             bytData[0] = (byte)(data1 >> 24  & 0xFF);
             bytData[1] = (byte)(data1 >> 16  & 0xFF);
             bytData[2] = (byte)(data1 >> 8  & 0xFF);
             bytData[3] = (byte)(data1 >> 0  & 0xFF);
             bytData[4] = (byte)(data2 >> 24  & 0xFF);
             bytData[5] = (byte)(data2 >> 16  & 0xFF);
             bytData[6] = (byte)(data2 >> 8  & 0xFF);
             bytData[7] = (byte)(data2 >> 0  & 0xFF);
             //key
             if (bytData[0] == 0x01)
             {
                 SendKey((KeyState)bytData[1],  (Keys)bytData[2], bytData[3], bytData[4] == 1, bytData[5] == 1,  bytData[6] == 1);
             }
             //mouse
             if (bytData[0] == 0x02)
             {
                 MouseButtons  button=MouseButtons.None;
                 if(bytData[2] ==1) button=  MouseButtons.Left;
                 if(bytData[3] ==1) button=  MouseButtons.Right;

                 SendMouse((MouseState)bytData[1],  button, bytData[4] << 8 | bytData[5], bytData[6] <<  8 | bytData[7]);
             }
         }
         public void Initialize (System.Windows.Forms.Dispatcher Dispatcher)
         {
             this.Dispatcher = Dispatcher;
             if (KeyEnable) Key_Initialize();
             if (MouseEnable) Mouse_Initialize();
             if (VirtualInputEnable)
             {
                 EventDispatcher eventDispatcher =  new EventDispatcher();
                 eventDispatcher.OnInterrupt += new  NativeEventHandler(eventDispatcher_OnInterrupt);
             }
         }
         public void SendKey(KeyState state, Keys  KeyCode, int KeyValue, bool Shift, bool Caps, bool Ins)
         {
             //Debug.Print(KeyValue.ToString()+" "+ ((int)state).ToString());
             if (Key != null) Key(state, KeyCode,  KeyValue, Shift, Caps, Ins);
         }
         public void SendMouse(MouseState  state,MouseButtons button, int x, int y)
         {
             //Debug.Print("(" + x.ToString() + ","  + y.ToString() + ")");
             if (Mouse != null) Mouse(state, button,  x, y);
    }

我們在開發基於WinForm庫的應用程序時,只要在Main函數中添加如下代碼, 即可以啟動該功能。

Application.InputProxy.VirtualInputEnable = true;

需要說明的是,該虛擬輸入和正常的輸入設備並沒有沖突(如觸摸屏及相關按 鍵),原先的輸入設備還是可以正常工作的,這樣做的目的,只是額外為你的設 備擴展了一個強大的輸入裝置。

還是那句話,開源後的.Net MF放飛了我們的夢想,通過簡單的擴展,使我們 和設備的交互的能力比以往更加強大和有力。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved