VC++位移操作>>和<<以及邏輯驅動器插拔產生的掩碼dbv.dbcv_unitmask進行分析的相關代碼

VC++位移操作>>和<<以及邏輯驅動器插拔產生的掩碼dbv.dbcv_unitmask進行分析的相關代碼

  • 一、VC++位移操作符<<和>>
    • 1、右位移操作符 >>:
    • 2、左位移操作符 <<:
  • 二、邏輯驅動器插拔產生的掩碼 dbv.dbcv_unitmask 進行分析的相關代碼
    • 1、設備發生改變的消息 WM_DEVICECHANGE
      • A、微軟官方 VC++ 代碼:
      • B、驅動器號與設備掩碼 dbcv_unitmask 的關系
    • 2、設備狀態發生改變 WM_DEVICECHANGE 的改進 C# 代碼:

一、VC++位移操作符<<和>>

1、右位移操作符 >>:

右移運算符>>,是將一個數的二進制位全部右移若干位,低位移出(舍棄)。高位的空位補符號位,即正數補零,負數補1。

語法格式: 需要移位的數字 >> 移位的次數

例如:
11 >> 2,等于2,是將數字11的二進制右移2位,即00001011------>00000010(2);
2 >> 1:相當于2/2
99 >> 1: 相當于 99/2 向下取整為49
99 >> 2:相當于 99/pow(2,2)向下取整為24
999 >> i:相當于999/pow(2,i)
整數 >> i:相當于將這個整數化為二進制整數,并去掉這個數的末尾的 i 位數字

cout << (2 >> 1) << endl;//輸出為 1
cout << (99 >> 1) << endl;//輸出為 49
cout << (99 >> 2) << endl;// 24
cout << (999 >> 3) << endl;// 124
cout << (1000 >> 4) << endl;// 62

2、左位移操作符 <<:

左移運算符<<,是將一個數的二進制位全部左移若干位,低位右補0,高位左移后溢出,舍棄。

語法格式: 需要移位的數字 << 移位的次數

例如:
將a的二進制數左移2位,右補0。
若a=15,即二進制數00001111,左移2位得00111100,即十進制數60。

3 << 2,則是將數字3左移2位。即00000011----->00001100(12);
2 << 1:相當于22。
99 << 1: 相當于 99
2 。
99 << 2:相當于 99*pow(2,2)。
999 << i:相當于999 * pow(2,i)。
整數 << i:相當于將這個整數化為二進制整數,并在這個數的末尾加上 i 個0。

cout << (2 << 1) << endl;    //輸出為 4
cout << (99 << 1) << endl;    //輸出為 198
cout << (99 << 2) << endl;    // 396
cout << (999 << 3) << endl;    // 7992
cout << (1000 << 4) << endl;    // 16000

數字1 << 左移:
1 << i = pow(2,i)
2 << i = pow(2,i) * 2
3 << i = pow(2,i) * 3
x << i = pow(2,i) * x

cout << (1 << 0) << endl;    // 1
cout << (1 << 1) << endl;    // 2
cout << (1 << 2) << endl;    // 4
cout << (1 << 3) << endl;    // 8
cout << (1 << 4) << endl;    // 16

二、邏輯驅動器插拔產生的掩碼 dbv.dbcv_unitmask 進行分析的相關代碼

1、設備發生改變的消息 WM_DEVICECHANGE

Windows 在設備發生的情況下,如可插拔設備 U盤、移動硬盤、USB串口的插入和彈出,光盤插入和彈出,會有相應的響應,在這里對于編程的識別進行探討:

Windows 定義了一些控制代碼和指令,當添加新設備或介質(如 CD 或 DVD)以及刪除現有設備或介質時,Windows 會給所有頂級窗口發送一組默認設備改變的消息 WM_DEVICECHANGE ,在微軟官方文檔中有具體描述:

檢測介質插入或刪除

每個 WM_DEVICECHANGE 消息都有描述更改的關聯事件,以及提供有關更改的詳細信息的結構。 該結構包含與事件無關的標頭 DEV_BROADCAST_HDR,后跟與事件相關的成員。 與事件相關的成員描述應用事件的設備。 若要使用此結構,應用程序必須首先確定事件類型和設備類型。 然后,它們可以使用正確的結構執行適當的操作。

當用戶將新的 CD 或 DVD 插入驅動器時,應用程序會收到 WM_DEVICECHANGE 消息和 DBT_DEVICEARRIVAL 事件。 應用程序必須檢查事件,以確保到達的設備的類型是卷(dbch_devicetype 成員是 DBT_DEVTYP_VOLUME),并且更換會影響介質(dbcv_flags 成員是 DBTF_MEDIA)。

當用戶從驅動器中刪除 CD 或 DVD 時,應用程序會收到 WM_DEVICECHANGE 消息和 DBT_DEVICEREMOVECOMPLETE 事件。 同樣,應用程序必須檢查事件,以確保要刪除的設備是卷,并且更換會影響介質。

A、微軟官方 VC++ 代碼:

//C++
#include <windows.h>
#include <dbt.h>
#include <strsafe.h>
#pragma comment(lib, "user32.lib" )void Main_OnDeviceChange( HWND hwnd, WPARAM wParam, LPARAM lParam );
char FirstDriveFromMask( ULONG unitmask );  //prototype/*------------------------------------------------------------------Main_OnDeviceChange( hwnd, wParam, lParam )描述:處理發送到應用程序頂級窗口的 WM_DEVICECHANGE 消息。
--------------------------------------------------------------------*/void Main_OnDeviceChange( HWND hwnd, WPARAM wParam, LPARAM lParam ){PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;TCHAR szMsg[80];switch(wParam ){case DBT_DEVICEARRIVAL:// 檢查驅動器中是否插入了CD或DVD。if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME)//驅動類型為邏輯卷{PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;if (lpdbv -> dbcv_flags & DBTF_MEDIA){StringCchPrintf( szMsg, sizeof(szMsg)/sizeof(szMsg[0]), TEXT("驅動器 %c :介質已插入。\n"), FirstDriveFromMask(lpdbv ->dbcv_unitmask) );MessageBox( hwnd, szMsg, TEXT("WM_DEVICECHANGE"), MB_OK );}}break;case DBT_DEVICEREMOVECOMPLETE:// 檢查是否已從驅動器中彈出CD或DVD。if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME)//驅動類型為邏輯卷{PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;if (lpdbv -> dbcv_flags & DBTF_MEDIA){StringCchPrintf( szMsg, sizeof(szMsg)/sizeof(szMsg[0]), TEXT("驅動器 %c: 介質已彈出。\n" ),FirstDriveFromMask(lpdbv ->dbcv_unitmask) );MessageBox( hwnd, szMsg, TEXT("WM_DEVICECHANGE" ), MB_OK );}}break;default:/*由于其他設備或原因,處理其他 WM_DEVICECHANGE 通知。*/ ;}
}/*------------------------------------------------------------------FirstDriveFromMask( unitmask )
描述
從驅動器號掩碼中查找第一個有效的驅動器號。
掩碼的格式必須為 bit 0=A、bit 1=B、bit 2=C,依此類推。
當相應的位設置為1時,將定義有效的驅動器號。
返回找到的第一個驅動器號。
--------------------------------------------------------------------*/char FirstDriveFromMask( ULONG unitmask ){char i;for (i = 0; i < 26; ++i){if (unitmask & 0x1)break;unitmask = unitmask >> 1;}return( i + 'A' );
}

B、驅動器號與設備掩碼 dbcv_unitmask 的關系

從上面C++代碼上可以看到對于位操作的右移指令 >>,從中可以看到驅動器掩碼與驅動器號的對應關系:

驅動器號字母值unitmaskunitmaskunitmask
卷號字母10進制數16進制數10進制數二進制數
A6511=2^010^00 =0000000000000001
B6622=2^110^01 =0000000000000010
C6744=2^210^02 =0000000000000100
D6888=2^310^03 =0000000000001000
E691016=2^410^04 =0000000000010000
F702032=2^510^05 =0000000000100000
G714064=2^610^06 =0000000001000000
H7280128=2^710^07 =0000000010000000
I73100256=2^810^08 =0000000100000000
J74200512=2^910^09 =0000001000000000
K754001024=2^1010^10 =000001000000000
L768002048=2^1110^11 =000010000000000
M7710004096=2^1210^12 =000100000000000
N7820008096=2^1310^13 =001000000000000
O79400016384=2^1410^14 =010000000000000
P80800032768=2^1510^15 =100000000000000
Q以此類推

從上表對應關系可以看出,對于驅動器號 A 盤的 unitmask 值,不能再執行右位移>>操作。

在對移動U盤和移動硬盤編寫的識別程序中,參照應用微軟以上 C++ 代碼遷移到 C# 中,可以發現只有一個分區的移動U盤和移動硬盤完全沒有問題。但對于移動U盤和移動硬盤有多個分區時,程序就無法完整識別(Windows 11),unitmask 值會隨著設備插入出現 2 個值,第一個值對應多分區的第一個盤符值,第二個值就無法用微軟提供的程序算法解釋了。通過對多分區移動盤的識別發現,如果移動盤符是 H、I、J、K 四個盤,系統給出了 2 個 unitmask 值,第一個值對應的第一個 H 盤,第二個值比對應的最后一個盤符 K 大,取整后就是 K 盤。

當設備被插入/拔出的時候,WINDOWS會向每個窗體發送WM_DEVICECHANGE 消息,當消息的wParam 值等于 DBT_DEVICEARRIVAL 時,表示Media設備被插入并且已經可用;如果wParam值等于DBT_DEVICEREMOVECOMPLETE,表示Media設備已經被移出。

它們的lParam都指向一個 DEV_BROADCAST_HDR結構體,其原形如下:

//C++
typedef struct _DEV_BROADCAST_HDR
{DWORD dbch_size;DWORD dbch_devicetype;DWORD dbch_reserved;
} DEV_BROADCAST_HDR, *PDEV_BROADCAST_HDR;

這個結構體僅僅是一個“頭”(HDR),其后還有附加數據,dbch_size表示結構體實例的字節數,當其中的dbch_devicetype字段值等于DBT_DEVTYP_VOLUME時,表示當前設備是邏輯驅動器,且lParam實際上指向的應該是DEV_BROADCAST_VOLUME 結構體實例,DEV_BROADCAST_VOLUME 結構體原形如下:

//C++
typedef struct _DEV_BROADCAST_VOLUME
{DWORD dbcv_size;DWORD dbcv_devicetype;DWORD dbcv_reserved;DWORD dbcv_unitmask;WORD dbcv_flags;
} DEV_BROADCAST_VOLUME, *PDEV_BROADCAST_VOLUME;

其中dbcv_unitmask 字段表示當前改變的驅動器掩碼,第一位表示驅動器號A,第二位表示驅動器號B,第三位表示驅動器號C,以此類推…… dbcv_flags 表示驅動器的類別,如果等于1,則是光盤驅動器;如果是2,則是網絡驅動器;如果是硬盤、U盤則都等于0

所以,我只需要在程序中捕捉WM_DEVICECHANGE 消息,然后根據具體情況去處理即可。

2、設備狀態發生改變 WM_DEVICECHANGE 的改進 C# 代碼:

//C# 響應消息聲明
public const int WM_DEVICECHANGE = 0x219;//U盤插入后,OS的底層會自動檢測到,然后向應用程序發送“硬件設備狀態改變“的消息
public const int DBT_DEVICEARRIVAL = 0x8000;  //就是用來表示U盤可用的。一個設備或媒體已被插入一塊,現在可用。
public const int DBT_DEVICEQUERYREMOVE = 0x8001;  //審批要求刪除一個設備或媒體作品。任何應用程序也不能否認這一要求,并取消刪除。
public const int DBT_DEVICEQUERYREMOVEFAILED = 0x8002;  //請求刪除一個設備或媒體片已被取消。
public const int DBT_DEVICEREMOVECOMPLETE = 0x8004;  //一個設備或媒體片已被刪除。
public const int DBT_DEVICEREMOVEPENDING = 0x8003;  //一個設備或媒體一塊即將被刪除。不能否認的。
//C#
public static List<char> Volumes = new List<char>();protected override void DefWndProc(ref Message m){if (m.Msg == WM_DEVICECHANGE)//設備發生改變{int wp = m.WParam.ToInt32();//存儲設備插/拔/彈if (wp == DBT_DEVICEARRIVAL || wp == DBT_DEVICEQUERYREMOVE || wp == DBT_DEVICEREMOVECOMPLETE || wp == DBT_DEVICEREMOVEPENDING) {DEV_BROADCAST_HDR dbhdr = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR));if (dbhdr.dbch_devicetype == 2)//驅動類型為DBT_DEVTYP_VOLUME 邏輯卷{DEV_BROADCAST_VOLUME dbv = (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));if (dbv.dbcv_flags == 0)//驅動器標識硬盤和U盤{char[] volums = GetVolumes(dbv.dbcv_unitmask);int len = volums.Length;int start = (int)volums[0];int end = (int)volums[len - 1];for (int i = start; i <= end; i++){string disk =((char)i).ToString() + ":";if(wp == DBT_DEVICEARRIVAL) //存儲設備插入{//插入設備后的操作}else if (wp == DBT_DEVICEREMOVECOMPLETE) //存儲設備拔/彈出{//拔出設備后的操作}} }}   else if (dbhdr.dbch_devicetype == 3)//DBT_DEVTYP_PORT=0x00000003 設備 (串行或并行) {//響應串口并口設備操作} }  } base.DefWndProc(ref m);           }                    /// <summary>根據驅動器掩碼返回驅動器號數組</summary>/// <param name="Mask">驅動器掩碼</param>/// <returns>返回驅動器號數組</returns>public static char[] GetVolumes(UInt32 Mask){for (int i = 0; i < 32; i++){//uint p = (uint)Math.Pow(2, i);//原參考代碼uint p = (uint)Math.Floor(Math.Log(Mask, 2));//算出驅動器掩碼 dbcv_unitmask 以2為底的指數,再取整加A的數值就是盤符。//if ((p | Mask) == p)//原參考代碼if (i == p){Volumes.Add((char)('A' + i));}}return Volumes.ToArray();}

移動 U盤的驅動類型是 Removable Media,
移動硬盤的驅動類型是 External hard disk media,
本地硬盤的驅動類型是 Fixed hard disk media。

在對以下 C# 代碼進行測試,插入后的多分區移動磁盤可以識別盤符,是對系統所有磁盤進行判斷,不能對插入、拔出進行響應。

//C#/// <summary>獲取U盤和可移動硬盤盤符名稱</summary>/// <returns></returns>public static List<string> GetDVN(){List<string> lstdisk = new List<string>();ManagementClass mgtcls = new ManagementClass("Win32_DiskDrive");var disks = mgtcls.GetInstances();foreach (ManagementObject mo in disks){if (mo.Properties["mediatype"].Value == null || mo.Properties["mediatype"].Value.ToString() == "External hard disk media" || mo.Properties["mediatype"].Value.ToString() == "Removable Media"){foreach (ManagementObject diskpartition in mo.GetRelated("win32_diskpartition")){foreach (ManagementObject disk in diskpartition.GetRelated("win32_logicaldisk")){lstdisk.Add(disk.Properties["name"].Value.ToString());}}// continue;}}return lstdisk;}

綜合上述代碼總結,可以對設備發生變化時進行的相應的程序操作。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/14407.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/14407.shtml
英文地址,請注明出處:http://en.pswp.cn/web/14407.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何使用Suno:免費的AI歌曲生成器

文章目錄 Suno AI 是什么&#xff1f;Suno AI 如何工作&#xff1f;選擇Suno AI的理由&#xff1a;核心優勢易于操作多樣化創作靈活的定價策略版權保障技術突破 如何使用Suno AI創作歌曲&#xff1f;第1步&#xff1a;注冊Suno AI賬戶第2步&#xff1a;輸入提示詞創建第 3 步&a…

作業-day-240522

思維導圖 使用IO多路復用實現并發 select實現TCP服務器端 #include <myhead.h>#define SER_IP "192.168.125.112" #define SER_PORT 8888int main(int argc, const char *argv[]) {int sfdsocket(AF_INET,SOCK_STREAM,0);if(sfd -1){perror("socket er…

脆皮之“字符函數與字符串函數”寶典

hello&#xff0c;大家好呀&#xff0c;感覺我之前有偷偷摸魚了&#xff0c;今天又開始學習啦。加油&#xff01;&#xff01;&#xff01; 文章目錄 1. 字符分類函數2. 字符轉換函數3. strlen的使用和模擬實現3.1 strlen 的使用3.1 strlen 的模擬1.計算器方法2.指針-指針的方…

Python的shutil模塊探索,文件操作的瑞士軍刀

hello&#xff0c;大家好&#xff0c;我是一點&#xff0c;專注于Python編程&#xff0c;如果你也對感Python感興趣&#xff0c;歡迎關注交流。 希望可以持續更新一些有意思的文章&#xff0c;如果覺得還不錯&#xff0c;歡迎點贊關注&#xff0c;有啥想說的&#xff0c;可以留…

每周刷題第三期

個人主頁&#xff1a;星紜-CSDN博客 系列文章專欄&#xff1a;Python 踏上取經路&#xff0c;比抵達靈山更重要&#xff01;一起努力一起進步&#xff01; 目錄 題目一&#xff1a;環形鏈表 題目二&#xff1a;刪除有序數組中的重復項 題目三&#xff1a;有效的括號 題…

從左上角到右下角的最小距離和

題目描述&#xff1a;給定一個二維數組matrix&#xff0c;一個人必須從左上角出發&#xff0c;最后到達右下角&#xff0c;沿途只可以向下或者向右走&#xff0c;沿途的數字都累加就是距離累加和&#xff0c;返回最小距離累加和。 way&#xff1a;無他&#xff0c;dp[i] [j]表…

《隊列》

描述 學校體操隊到操場集合&#xff0c;排成每行2人&#xff0c;最后多出1人;排成每行3人&#xff0c;也多出1人。分別排成每行4、5、6人&#xff0c;都多出1人。當排成每行7人時&#xff0c;正好不多,求校體操隊至少多少人。 輸入描述 無 輸出描述 滿足要求的人數 樣例輸入…

Python語法學習之 - 生成器表達式(Generator Expression)

第一次見這樣的語法 本人之前一直是Java工程師&#xff0c;最近接觸了一個Python項目&#xff0c;第一次看到如下的代碼&#xff1a; i sum(letter in target_arr for letter in source_arr)這條語句是計算source 與 target 數組中有幾個單詞是相同的。 當我第一眼看到這樣…

shell遍歷路徑所有文件并把列表寫成字符串遍歷

1. ls dir/* | tr ‘\n’ ’ ’ 換行替換成空格 你可以使用 ls 命令和 tr 命令來將文件列表根據空格拼接起來成一個字符串。以下是一個示例&#xff1a; ls dir/* | tr \n 解釋 ls dir/*&#xff1a;列出 dir 目錄下的所有文件。tr \n &#xff1a;將所有的換行符&#xf…

ChatGPT生成常見面試題【面試準備】

ChatGPT生成常見面試題【面試準備】 前言版權ChatGPT生成常見面試題【面試準備】MySQL面試問題與回答1. 數據庫連接與操作2. 索引和查詢優化3. 事務管理4. 索引是什么&#xff1f;為什么使用索引可以提高查詢性能&#xff1f;如何在MySQL中創建索引&#xff1f;5. SQL查詢優化有…

Varjo XR-4功能詳解:由凝視驅動的XR自動對焦相機系統

Varjo是XR市場中擁有領先技術的虛擬現實設備供應商&#xff0c;其將可變焦距攝像機直通系統帶入到虛擬和混合現實場景中。在本篇文章中&#xff0c;Varjo的技術工程師維爾蒂莫寧詳細介紹了這項在Varjo XR-4焦點版中投入應用的技術。 對可變焦距光學系統的需求 目前所有其他XR頭…

WPF之容器標簽之Canvas布局標簽

Canvas: 定義一個區域&#xff0c;可在其中使用相對于 Canvas 區域的坐標以顯式方式來定位子元素。 實例 可以在子標簽使用Canvas屬性設置定位 <Canvas Width"500" Height"300"><StackPanel Width"100" Height"100"Backgro…

網頁抓取之requests庫的使用

Python網絡數據采集利器 - Requests庫的使用指南 簡介 在Python網絡爬蟲領域,優秀的第三方庫Requests可謂是必學的重要工具。它提供了相當人性化的API,讓我們能夠用極其簡潔的代碼發送HTTP/HTTPS請求,并且自動處理cookies、headers、編碼等諸多繁瑣細節,大大減輕了網頁抓取的…

【pdb的使用方法】

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 一、 pdb 是什么&#xff1f;二、基本用法1.啟動 PDB 調試器&#xff1a;2.單步執行代碼&#xff1a;3.查看變量值&#xff1a;4.退出調試器&#xff1a; 三、高級用…

指數分布的理解,推導與應用

指數分布的定義 在浙大版的教材中&#xff0c;指數分布的定義如下&#xff1a; 若連續型的隨機變量 X X X的概率密度為&#xff1a; f ( x ) { 1 θ e ? x θ , x>0 0 , 其他 f(x) \begin{cases} \frac{1}{\theta} e^{-\frac{x}{\theta}}, & \text{x>0}\\ 0, &a…

mvn編譯所有單元測試報錯OOM

org.mockito.exceptions.base.MockitoException: Cannot instantiate InjectMocks field named ‘productLogic’ of type ‘class .ProductLogic’. You haven’t provided the instance at field declaration so I tried to construct the instance. However the constructo…

Python正則表達式與Excel文件名批量匹配技術文章

目錄 引言 正則表達式基礎 Python中的re模塊 Excel文件名批量匹配案例 常見問題與解決方案 結論 引言 在現代辦公環境中&#xff0c;Excel文件幾乎成為了數據分析和處理的標配工具。由于Excel文件可能包含大量的數據和信息&#xff0c;因此&#xff0c;對Excel文件的命名…

在aspNetCore中 使用System.Text.Json的定制功能, 將定制化的json返回給前端

C# 默認大寫, 而大部分的前端默認小寫, 這時候可以如此配置: builder.Services.AddControllers().AddJsonOptions((opt) > {opt.JsonSerializerOptions.PropertyNamingPolicy System.Text.Json.JsonNamingPolicy.CamelCase;opt.JsonSerializerOptions.WriteIndented true…

DSPF網絡類型實驗1

對R6配置 對R1配置 對R2 對R3 對R4 對R5 對R1R2R3R4R5加用戶 環回處理 然后開始配置缺省 R1有兩個下一跳 3&#xff0c;4&#xff0c;5同R2 然后對R1 dynamic動態 對R2 手寫 把注冊加上 register R3同R2處理