Winform虛擬/模擬鍵盤

来源:https://www.cnblogs.com/lotusgu/archive/2023/05/23/17422796.html
-Advertisement-
Play Games

在使用 using 等新語法時,在 VisualStudio 2019 會自動判斷框架版本,如在 net 45 就不會自動使用最新版本的語法,需要修改項目文件 在使用 C# 8.0 之前,請在官網 下載最新的 VisualStudio 2019 版本 如果在編譯時提示 “Using 聲明”在 C# ...


項目需要在觸摸屏上增加一個虛擬鍵盤。記錄下過程中遇到的問題及解決方法。

1.模擬按鍵

網上找到如下3種方法

1)SendKeys.Send

測試單獨的shift不好用,所以最終未採納此方法

SendKeys.Send("^{E}");//shift+e
SendKeys.Send("{Enter}");

2)keybd_event

最終選擇了這個方法,簡單有效。

虛擬按鍵對照表:https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes

[DllImport("user32.dll", EntryPoint = "keybd_event", SetLastError = true)]
public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
[DllImport("user32.dll", EntryPoint = "keybd_event", SetLastError = true)]
public static extern void keybd_event(Keys bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
public static uint KEYEVENTF_KEYDOWN = 0;// 鍵按下 KEYEVENTF_KEYDOWN = 0
public static uint KEYEVENTF_KEYUP = 2;//鍵彈起 KEYEVENTF_KEYUP = 2
keybd_event(0x14, 0, KEYEVENTF_KEYDOWN, 0); //鍵按下 KEYEVENTF_KEYDOWN = 0
keybd_event(0x14, 0, KEYEVENTF_KEYUP, 0); //鍵彈起 KEYEVENTF_KEYUP = 2

3)PostMessage

這個方法也是簡單有效,但是據說有些其他問題,沒有仔細研究,有興趣的可以自己研究。

[DllImport("user32.dll", EntryPoint = "PostMessageA", SetLastError = true)]
public static extern int PostMessage(IntPtr hWnd, int Msg, Keys wParam, int lParam);

PostMessage(textBox2.Handle, 256, Keys.D, 2);//模擬按下2次 Keys.D

2.各種類型鍵的處理

微軟對鍵盤輸入有進行了分類,可以參考這個。

https://learn.microsoft.com/zh-cn/windows/win32/learnwin32/keyboard-input

1)CapsLock鍵/NumLock鍵

想要實現鍵盤按下抬起與界面效果相同,就需要捕獲鍵盤操作。我使用了Hook

以下這篇博客寫的很清晰明瞭,就不贅述了。

https://www.cnblogs.com/chorm590/p/14199978.html

在hook回調函數中做瞭如下處理

private int keyboardHookCallback(int code, IntPtr wParam, IntPtr lParam)
{
  if (code < 0)
    {
       return User32.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
    }
   else
   {
     Keyboard_LL_Hook_Data khd = (Keyboard_LL_Hook_Data)Marshal.PtrToStructure(lParam, typeof(Keyboard_LL_Hook_Data));
      System.Diagnostics.Debug.WriteLine($"key event:{wParam}, key code:{khd.vkCode}, event time:{khd.time}");
      if ((int)wParam == 256 && khd.vkCode == 0x14)
      {
        IsCapsLockDown = !IsCapsLockDown;
      }
      return User32.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
   }
}
private bool isCapsLockDown;
private bool IsCapsLockDown
{
  get { return isCapsLockDown; }
   set
    {
      isCapsLockDown = value;
       this.buttonCapsLock.BackColor = value ? Color.Gray : Control.DefaultBackColor;
    }
}
private void buttonCapsLock_Click(object sender, EventArgs e)
{
  User32.keybd_event(VirtualKeyCode.CAPS_LOCK, 0, 0, 0);   //鍵按下 KEYEVENTF_KEYDOWN = 0
   User32.keybd_event(VirtualKeyCode.CAPS_LOCK, 0, 2, 0);  //鍵彈起 KEYEVENTF_KEYUP = 2
}

初始化時同步鍵盤CapsLock/NumLock狀態,使用user32.GetKeyState.返回值等於1則按下狀態

[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern short GetKeyState(int keyCode);
public static bool GetState(byte VKeyCode)
{
   return (User32.GetKeyState(VKeyCode) == 1);
}

2)其他組合鍵處理

ctrl+shift切換輸入法,只響應這一種功能鍵+功能鍵。其他小伙伴有好的方法可以分享一下。

3)輸入鍵+組合鍵

如ctrl+c,ctrl+v,ctrl+a,ctrl+z等等。

此方法不是最優方式,可以改進功能鍵存放在Queue中,遍歷功能鍵,按順序響應。

private void HandleKeysCombin(byte VKCode)
{
  //設置焦點控制項
   this.ActiveControl = this.m_CustomActiveControl;
   lock (this)
   {
     if (IsCtrlDown)
      {
        keybd_event(VirtualKeyCode.CTRL, 0, KEYEVENTF_KEYDOWN, 0);                //鍵按下 KEYEVENTF_KEYDOWN = 0
      }
      if (IsShiftDown)
      {
        keybd_event(VirtualKeyCode.SHIFT, 0, KEYEVENTF_KEYDOWN, 0);                //鍵按下 KEYEVENTF_KEYDOWN = 0
      }
      if (IsAltDown)
      {
        keybd_event(VirtualKeyCode.ALT, 0, KEYEVENTF_KEYDOWN, 0);                //鍵按下 KEYEVENTF_KEYDOWN = 0
      }
      keybd_event(VKCode, VKCode, User32.KEYEVENTF_KEYDOWN, 0);                //鍵按下 KEYEVENTF_KEYDOWN = 0
      keybd_event(VKCode, VKCode, User32.KEYEVENTF_KEYUP, 0);           //鍵彈起 KEYEVENTF_KEYUP = 2
      if (IsCtrlDown)
      {
        keybd_event(VirtualKeyCode.CTRL, 0, KEYEVENTF_KEYUP, 0);                //鍵彈起 KEYEVENTF_KEYUP = 2
         IsCtrlDown = false;
      }
      if (IsShiftDown)
      {
        keybd_event(VirtualKeyCode.SHIFT, 0, KEYEVENTF_KEYUP, 0);              //鍵彈起 KEYEVENTF_KEYUP = 2
         IsShiftDown = false;
      }
      if (IsAltDown)
      {
         keybd_event(VirtualKeyCode.ALT, 0, KEYEVENTF_KEYUP, 0);                //鍵彈起 KEYEVENTF_KEYUP = 2
        IsAltDown = false;
     }
  }
}

3.窗體/UserControl焦點的處理

這篇裡面方法簡單好用,不贅述

 https://blog.csdn.net/accomp/article/details/7209052


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 有時間,我們在搭建微服務時,總希望拿一個比較單純的,沒有污染其它代碼的項目來從頭開始做,今天我們來建設一個最簡單的,gateway項目,它被註冊到nacos里,路由配置也存到nacos里,動態實現更新配置功能。 # 依賴配置 > 版本:com.alibaba.cloud:spring-cloud-s ...
  • 摘要:常用於消除雜訊的圖像平滑方法包括三種線性濾波(均值濾波、方框濾波、高斯濾波)和兩種非線性濾波(中值濾波、雙邊濾波),本文將詳細講解三種線性濾波方法。 本文分享自華為雲社區《[Python從零到壹] 五十五.圖像增強及運算篇之圖像平滑(均值濾波、方框濾波、高斯濾波)》,作者:eastmount。 ...
  • 學習的文章 [小姐姐非要問我:spring編程式事務是啥? (qq.com)](https://mp.weixin.qq.com/s?__biz=MzA5MTkxMDQ4MQ==&mid=2648936779&idx=2&sn=a6255c7d436a62af380dfa6b326fd4e7&chk ...
  • Java applet 不知道有同學聽過嗎?我也只是聽過,並沒有使用過。我特意去瞭解了一下它,本文就對 Java applet 進行簡單介紹,說說它的輝煌與衰敗。僅此而已,現在已經沒人使用 Java applet 開發了。 ...
  • ## 1、概述 我們常說的JMM指的是Java記憶體模型(Java Memory Model,JMM),主要用於控制Java程式解決線程間如何通信和數據同步,JMM規範了多線程訪問共用記憶體時的 **可見性、有序性和原子性**。 - 所有的共用變數都存在**主記憶體**中; - **每個線程**都保存了一 ...
  • 以前就是一直使用 `Newtonsoft.Json` 用起來還是挺舒服的。由於 JSON 的應用越來越廣,現在. NET Core 都內置了 `System.Text.Json` 可以直接對 JSON 進行操作,不過兩個東西的體驗依然有點區別。 有時候我們會遇到的從第三方傳遞過來的 json str ...
  • C# 讀取網路上下行有多種方式,其中有一種是使用System.Net.NetworkInformation命名空間中的NetworkInterface類和PerformanceCounter類,該方式其實讀的是windows系統的性能計數器中的Network Interface類別的數據。 方式如下 ...
  • 遞歸演算法本質: 1、方法的自我調用 2、有明確的終止條件 3、每次調用時,問題規模在不斷減少。通過遞減,最終到達終止條件 ...
一周排行
    -Advertisement-
    Play Games
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...