Dùng WMI/CIM từ C# và PowerShell — hướng dẫn thực hành lấy thông tin phần cứng, giám sát tiến trình và truy vấn từ xa
· Go Komura · Windows, C#, .NET, PowerShell, WMI, CIM, Ứng dụng nghiệp vụ, Phát triển Windows
“Tôi muốn hiện số serial và tên model của PC trên màn hình ứng dụng nghiệp vụ.” “Tôi muốn giám sát dung lượng đĩa trống của máy chủ và cảnh báo.” “Tôi muốn phát hiện khi một tiến trình cụ thể khởi chạy.” “Tôi muốn gom trạng thái các PC ở nơi xa về một chỗ.” — Khi xây ứng dụng nghiệp vụ Windows và công cụ quản lý, loại yêu cầu này là chuyện thường gặp. Và câu trả lời chuẩn cho chúng là WMI (Windows Management Instrumentation), hay, theo tên chuẩn, CIM (Common Information Model).
flowchart TB
accTitle: Yêu cầu thường gặp và WMI/CIM
accDescr: Câu trả lời chuẩn cho các yêu cầu thường gặp của ứng dụng nghiệp vụ gồm hiện số serial và tên model, giám sát dung lượng đĩa trống, phát hiện tiến trình khởi chạy, truy vấn PC từ xa là WMI, hay CIM theo tên chuẩn
r1["Số serial · tên model"] --> ans["WMI(tên chuẩn là CIM)"]
r2["Giám sát dung lượng đĩa trống"] --> ans
r3["Phát hiện tiến trình khởi chạy"] --> ans
r4["Truy vấn PC từ xa"] --> ans
Hình 1: Câu trả lời chuẩn cho bốn yêu cầu thường gặp của ứng dụng nghiệp vụ là WMI/CIM.
Điều gây khó là thông tin quanh WMI trộn lẫn cũ và mới. Tìm kiếm thì bài mười năm tuổi dùng Get-WmiObject ngồi cạnh bài dùng Get-CimInstance, phía C# lại có hai dòng System.Management và Microsoft.Management.Infrastructure. Cái nào là cách viết hiện hành, cái nào là “vẫn chạy nhưng không chọn cho mã mới” thì khó nhìn ra. Thực tế, Get-WmiObject không tồn tại trên PowerShell 7, và điều đó đột ngột lộ ra khi di chuyển tập lệnh nội bộ viết cho 5.1.
Bài này hướng tới nhà phát triển C#/PowerShell đang gắn lấy thông tin phần cứng, giám sát tiến trình và truy vấn PC từ xa vào ứng dụng nghiệp vụ. Nó đi từ hiểu tối thiểu cấu trúc WMI/CIM, qua cmdlet CIM của PowerShell, hai API C#, công thức thực tế hay dùng, các cạm bẫy về hiệu năng, quyền và 64-bit, rồi cách phán “khi nào không nên dùng WMI” — tất cả sắp xếp từ nguồn gốc tính tới tháng 8 năm 2026.
1. Kết luận trước
- CIM là mô hình chuẩn ngành về thông tin quản lý do DMTF soạn thảo, WMI là triển khai của Microsoft cho chuẩn đó. API họ “CIM” trong PowerShell và C# là thế hệ hiện hành bám chuẩn này, và chúng kết nối tới cùng nền tảng WMI.1
- Trên PowerShell, thế hệ hiện hành là cmdlet CIM (Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent). Các cmdlet WMI cũ (Get-WmiObject và bốn cái nữa) đã bị xóa từ PowerShell 6 trở đi và không chạy trên PowerShell 7.2
- Không gian tên mặc định là root/CIMV2, và truy vấn hàng ngày cơ bản là lọc các lớp Win32_* ở đó bằng WQL.3
- Truy vấn từ xa mặc định dùng WSMan (WinRM). Chỉ định
-ComputerNametạo phiên tạm WSMan. Nếu bạn truy vấn cùng một đích nhiều lần, tái sử dụng phiên CIM (New-CimSession) là phép tắc về hiệu năng, và với máy cũ không cấu hình được WinRM thì có tùy chọn giao thức DCOM.34 - C# có hai dòng: System.Management (ManagementObjectSearcher) và Microsoft.Management.Infrastructure (CimSession). Cả hai đều chỉ dành cho Windows, và trên .NET hiện hành bạn đưa chúng vào qua NuGet. Nếu gắn truy vấn từ xa hay giám sát vào sản phẩm thật, MI API — cùng hệ kiểu với cmdlet CIM — hợp hơn.56
- Phát hiện tiến trình khởi chạy bằng đăng ký sự kiện, không polling. Đăng ký
Win32_ProcessStartTracephải chạy với quyền quản trị viên.78 - Đừng dùng
SELECT *theo thói quen. Lọc dữ liệu chuyển đi bằng-Filter/-Property/-KeyOnlyngăn khoảng nửa vấn đề hiệu năng của WMI.3 - WMI không phải lời giải vạn năng. Với giám sát hiệu năng tần suất cao, đọc ghi thiết lập của chính ứng dụng, hay gọi chức năng OS một lần, bộ đếm hiệu năng, registry, Win32 API, hoặc cmdlet chuyên biệt hợp hơn (xem bảng quyết định ở mục 8).
2. WMI/CIM là gì — chuẩn và triển khai, không gian tên, lớp, WQL
Trước hết, sắp xếp quan hệ thuật ngữ một lần cho xong.
| Thuật ngữ | Bản chất |
|---|---|
| CIM (Common Information Model) | Mô hình chuẩn ngành để biểu diễn đối tượng quản lý như hệ thống, ứng dụng, mạng và thiết bị. DMTF (Distributed Management Task Force) soạn thảo và duy trì1 |
| WBEM (Web-Based Enterprise Management) | Sáng kiến ngành nhằm tạo công nghệ chuẩn để truy cập thông tin quản lý trong môi trường doanh nghiệp1 |
| WMI | Triển khai của Microsoft cho WBEM. Biểu diễn đối tượng quản lý bằng chuẩn CIM và được gắn sẵn trong Windows1 |
| MI (Windows Management Infrastructure) | Thế hệ kế của WMI. Tương thích hoàn toàn với WMI cũ, và hầu hết provider mới được viết bằng MI1 |
flowchart TB
accTitle: Quan hệ chuẩn CIM và triển khai WMI
accDescr: Chuẩn CIM do DMTF soạn thảo và duy trì được dùng trong khuôn khổ sáng kiến WBEM, triển khai của Microsoft là WMI, MI thế hệ kế tương thích hoàn toàn với WMI cũ, và API họ CIM kết nối tới cùng nền tảng WMI
dmtf["DMTF soạn thảo và duy trì"] --> cim["CIM(mô hình chuẩn ngành)"]
wbem["WBEM(sáng kiến ngành)"] --> wmi["WMI(triển khai của Microsoft)"]
cim --> wmi
wmi -.-> mi["MI(thế hệ kế · tương thích hoàn toàn)"]
api["API họ CIM(PowerShell / C#)"] --> wmi
Hình 2: CIM là đặc tả, WMI là triển khai trên Windows. API họ CIM kết nối tới cùng nền tảng WMI.
Với tư cách nhà phát triển, bốn mảnh cấu trúc đáng nắm là như sau.
- Không gian tên (namespace): phân cấp gom các lớp lại. Truy vấn hàng ngày gần như luôn dùng root/CIMV2, cũng là mặc định của cmdlet CIM.3 Ngoài ra còn
root\default(provider registry, v.v.). - Lớp: kiểu biểu diễn đối tượng quản lý, như
Win32_ComputerSystem(máy tính),Win32_LogicalDisk(ổ logic), hayWin32_Process(tiến trình). Các lớp riêng Windows kế thừa lớp chuẩn CIM (nhưCIM_LogicalDisk) mang tiền tốWin32_.9 - Provider: thành phần cung cấp thực thể của lớp. Khi bạn truy vấn, provider hỏi OS ngay tại chỗ và dựng giá trị.
- WQL: ngôn ngữ truy vấn giống SQL. Như
SELECT Name, State FROM Win32_Service WHERE StartMode = 'Auto', nó coi lớp như bảng rồi lọc. WQL cũng là ngôn ngữ truy vấn mặc định của cmdlet CIM.3
“Đọc thông tin OS và phần cứng qua một bộ lớp thống nhất và một ngôn ngữ truy vấn” — đó là giá trị WMI mang lại. Ngược lại, ghi và thao tác bị giới hạn ở một số lớp có phương thức gọi được qua Invoke-CimMethod; đây không phải cơ chế làm được mọi thứ.
flowchart TB
accTitle: Cấu trúc truy vấn WMI
accDescr: Truy vấn WQL hướng tới lớp Win32_* trong không gian tên root/CIMV2, provider cung cấp thực thể của lớp hỏi OS ngay tại chỗ để dựng giá trị, rồi kết quả được trả về
wql["Truy vấn bằng WQL"] --> ns["Không gian tên root/CIMV2"]
ns --> cls["Lớp Win32_*"]
cls --> prov["Provider"]
prov --> osq["Hỏi OS ngay tại chỗ"]
osq --> res["Trả kết quả"]
Hình 3: Truy vấn lần theo không gian tên, lớp, rồi provider, và giá trị được dựng tại chỗ.
3. Dùng từ PowerShell — cmdlet CIM là hiện hành, cmdlet WMI đã bị xóa
3.1. Nền tảng: Get-CimInstance
# Theo tên lớp (không gian tên mặc định root/CIMV2)
Get-CimInstance -ClassName Win32_OperatingSystem
# Chỉ viết nội dung mệnh đề WHERE vào -Filter (đừng viết từ khóa WHERE)
Get-CimInstance -ClassName Win32_Service -Filter "StartMode = 'Auto' AND State <> 'Running'"
# Chỉ lấy thuộc tính cần thiết để giảm lượng truyền
Get-CimInstance -ClassName Win32_Process -Property Name, ProcessId, CreationDate
# Dùng -Query nếu muốn viết WQL nguyên văn
Get-CimInstance -Query "SELECT * FROM Win32_Process WHERE Name LIKE 'p%'"
-Filter chính là mệnh đề WHERE của WQL, còn -Property hạn chế cột lấy về.3 Giá trị trả về là đối tượng CimInstance, và thuộc tính ngày (như CreationDate hay LastBootUpTime) đã được chuyển thành DateTime. Khác Get-WmiObject cũ, đối tượng lấy về không mang phương thức trực tiếp, nên gọi phương thức bằng cách đưa vào Invoke-CimMethod.
flowchart TB
accTitle: Gọi phương thức trên CimInstance
accDescr: CimInstance do Get-CimInstance trả về đã chuyển thuộc tính ngày thành DateTime nhưng không mang phương thức trực tiếp, nên gọi phương thức bằng cách đưa instance vào Invoke-CimMethod
gci["Get-CimInstance"] --> inst["Đối tượng CimInstance"]
inst -.-> dt["Ngày đã chuyển thành DateTime"]
inst -.-> nom["Không mang phương thức trực tiếp"]
inst --> icm["Đưa vào Invoke-CimMethod"]
icm --> call["Gọi phương thức"]
Hình 4: CimInstance không mang phương thức nên gọi phương thức bằng cách đưa vào Invoke-CimMethod.
# Gọi phương thức trên thể hiện: lấy chủ mỗi tiến trình
Get-CimInstance -ClassName Win32_Process -Filter "Name = 'notepad.exe'" |
Invoke-CimMethod -MethodName GetOwner
# Gọi phương thức tĩnh của lớp: khởi chạy tiến trình
Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = 'notepad.exe' }
# Xem định nghĩa lớp (thuộc tính và phương thức)
Get-CimClass -ClassName Win32_Process
3.2. Bảng tương ứng khi di chuyển từ cmdlet WMI cũ
Từ PowerShell 6 trở đi (gồm PowerShell 7 hiện hành), các cmdlet WMI v1 sau đã bị xóa. Cùng chức năng được module CimCmdlets (WMI v2) cung cấp.2
| Cũ (tới Windows PowerShell 5.1) | Hiện hành (cmdlet CIM) | Ghi chú |
|---|---|---|
Get-WmiObject |
Get-CimInstance |
Cách nghĩ -Filter / -Query giống nhau |
Get-WmiObject -List |
Get-CimClass |
Khám phá lớp và xem định nghĩa |
Invoke-WmiMethod |
Invoke-CimMethod |
Đối số đưa qua hashtable -Arguments @{ } |
Register-WmiEvent |
Register-CimIndicationEvent |
Đăng ký sự kiện (mục 6.3) |
Set-WmiInstance |
Set-CimInstance |
Đổi thuộc tính ghi được |
Remove-WmiObject |
Remove-CimInstance |
Xóa instance |
Cmdlet CIM cũng chạy được trên Windows PowerShell 5.1, nên cái viết mới nên viết phía CIM ngay cả khi sẽ chạy trên 5.1 — như vậy không để lại chi phí di chuyển. Toàn cảnh cùng tồn tại và di chuyển giữa 5.1 và 7 nằm trong “Khác biệt giữa Windows PowerShell 5.1 và PowerShell 7”.
flowchart TB
accTitle: Lý do viết tập lệnh mới bằng CIM
accDescr: Tập lệnh viết bằng cmdlet WMI chạy trên 5.1 nhưng đã bị xóa từ PowerShell 6 nên phải viết lại khi di chuyển, còn cmdlet CIM dùng được cả trên 5.1 nên viết mới phía CIM thì không để lại chi phí di chuyển
new["Tập lệnh viết mới"] --> q1{"Viết bằng cái nào?"}
q1 -->|Cmdlet WMI| old["Chạy được trên 5.1"]
q1 -->|Cmdlet CIM| cur["Dùng được cả trên 5.1"]
old --> del["Đã bị xóa trên PowerShell 7"]
del --> rew["Phải viết lại khi di chuyển"]
cur --> norew["Không để lại chi phí di chuyển"]
Hình 5: Viết mới bằng cmdlet CIM thì khi sang PowerShell 7 không phải viết lại.
4. Truy vấn từ xa — phiên CIM (WSMan mặc định) và tùy chọn DCOM
Cmdlet CIM, nếu không chỉ định đích, kết nối tới WMI cục bộ qua COM; nếu chỉ định -ComputerName, chúng tạo phiên tạm qua giao thức WSMan (WinRM) để kết nối. Khi thực hiện nhiều thao tác trên cùng một máy, tạo phiên CIM rồi tái sử dụng có lợi hơn về hiệu năng.3
flowchart TB
accTitle: Chọn cách kết nối CIM
accDescr: Không chỉ định thì kết nối COM tới WMI cục bộ, chỉ định ComputerName thì phiên tạm WSMan được tạo mỗi lần truy vấn, nhiều thao tác trên cùng đích thì tái sử dụng New-CimSession có lợi về hiệu năng, và máy chưa cấu hình WinRM có tùy chọn giao thức DCOM
exec["Chạy cmdlet CIM"] --> q1{"Có chỉ định ComputerName?"}
q1 -->|Không| local["Kết nối COM tới WMI cục bộ"]
q1 -->|Có| q2{"Nhiều thao tác trên cùng máy?"}
q2 -->|Một lần| temp["Phiên tạm WSMan"]
q2 -->|Nhiều| sess["Tái sử dụng New-CimSession"]
temp -.-> cost["Được tạo mỗi lần truy vấn"]
nowinrm["Máy chưa cấu hình WinRM"] -.-> dcom["Tùy chọn giao thức DCOM"]
Hình 6: Truy vấn từ xa mặc định dùng WSMan, và nhiều thao tác trên cùng đích thì tái sử dụng phiên CIM là phép tắc.
# Truy vấn một lần thì dùng -ComputerName (mỗi lần tạo phiên tạm)
Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName Server01, Server02
# Truy vấn lặp lại thì tái sử dụng phiên CIM
$session = New-CimSession -ComputerName Server01
Get-CimInstance -ClassName Win32_OperatingSystem -CimSession $session
Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType = 3" -CimSession $session
Remove-CimSession $session
Với máy không tới được bằng WSMan — như máy cũ không cấu hình được WinRM — bạn có thể chọn giao thức DCOM.4
$dcom = New-CimSessionOption -Protocol Dcom
$session = New-CimSession -ComputerName OldServer -SessionOption $dcom
Điều kiện tiên quyết của truy vấn từ xa như sau.
- WinRM phải được cấu hình trên máy đích.
winrm quickconfigđặt dịch vụ tự khởi động, tạo listener HTTP (cổng mặc định 5985), và đăng ký ngoại lệ tường lửa, tất cả một lần.10 Nếu muốn kết nối HTTPS (cổng mặc định 5986), chỉ vậy chưa đủ — cần chuẩn bị chứng chỉ máy chủ rồi cấu hình riêng listener HTTPS bằngwinrm quickconfig -transport:httpshoặc tương tự.10 - Các cổng liên quan phải mở trên tường lửa trên đường đi. Công việc thiết kế và đăng ký quy tắc vào được trình bày trong bài “Windows Firewall và ứng dụng nghiệp vụ”.
- Xác thực. Trong môi trường domain, Kerberos cung cấp xác thực lẫn nhau. Trong workgroup, Kerberos không dùng được, nên bạn có thể cần đăng ký đích vào
TrustedHostsphía máy khách. Giữ danh sách đó hẹp hết mức có thể.10 - Quyền. Với cấu hình mặc định, truy vấn và thao tác WMI từ xa về cơ bản dùng tài khoản thuộc nhóm quản trị viên trên máy đích. Muốn mở cho người dùng thường, cần cấu hình quyền truy cập trên cả WinRM lẫn không gian tên WMI.10
- Lưu ý rằng DCOM không có cổng lắng nghe cố định (nó dùng cổng RPC động), nên thiết kế xuyên tường lửa khó hơn. An toàn nhất là coi WSMan là mặc định cho thứ bạn xây từ nay.
flowchart TB
accTitle: Kiểm tra điều kiện tiên quyết của truy vấn từ xa
accDescr: Trên máy đích winrm quickconfig đặt dịch vụ tự khởi động, tạo listener HTTP và đăng ký ngoại lệ tường lửa một lần, listener HTTPS cần chuẩn bị chứng chỉ rồi cấu hình riêng, và môi trường workgroup đôi khi cần đăng ký TrustedHosts
qc["winrm quickconfig"] --> svc["Đặt dịch vụ tự khởi động"]
qc --> lis["Tạo listener HTTP(5985)"]
qc --> fw["Ngoại lệ tường lửa"]
lis ~~~ https["Listener HTTPS(5986)"]
https -.-> cert["Chuẩn bị chứng chỉ và cấu hình riêng"]
fw ~~~ wg["Môi trường workgroup"]
wg -.-> th["Đăng ký vào TrustedHosts"]
Hình 7: winrm quickconfig làm cấu hình mặc định một lần, còn listener HTTPS và xác thực workgroup xử lý riêng.
5. Dùng từ C# — System.Management và Microsoft.Management.Infrastructure
Có hai dòng API để dùng WMI từ C#. Cả hai đều chỉ dành cho Windows.
| System.Management | Microsoft.Management.Infrastructure (MI API) | |
|---|---|---|
| Đưa vào | Mặc định trong .NET Framework. Trên .NET hiện hành, gói NuGet System.Management5 | Gói NuGet Microsoft.Management.Infrastructure6 |
| Lớp cửa vào | ManagementObjectSearcher (đưa WQL để truy vấn)5 |
CimSession (Create → QueryInstances / InvokeMethod / Subscribe)6 |
| Hệ kiểu | ManagementObject / ManagementEventWatcher11 |
CimInstance / CimSession — cùng kiểu với cmdlet CIM3 |
| Từ xa | Dựa trên DCOM | Dựa trên WSMan (phiên CIM). Có biến thể bất đồng bộ (*Async)6 |
| Hợp khi | Lấy thông tin cục bộ. Giữ tài sản mã sẵn có | Gắn truy vấn từ xa và giám sát. Thiết kế đi cùng PowerShell |
5.1. System.Management: nền tảng ManagementObjectSearcher
Bạn đưa WQL dạng chuỗi và nhận bộ sưu tập kết quả qua Get().5
// NuGet: System.Management (chỉ Windows)
using System.Management;
using var searcher = new ManagementObjectSearcher(
@"root\cimv2",
"SELECT DeviceID, FreeSpace, Size FROM Win32_LogicalDisk WHERE DriveType = 3");
foreach (ManagementObject disk in searcher.Get())
{
var freeGb = (ulong)disk["FreeSpace"] / 1024.0 / 1024.0 / 1024.0;
var sizeGb = (ulong)disk["Size"] / 1024.0 / 1024.0 / 1024.0;
Console.WriteLine($"{disk["DeviceID"]} trống {freeGb:F1} GB / tổng {sizeGb:F1} GB");
}
Thuộc tính trả về dưới dạng object qua indexer, nên bạn cần xem tài liệu lớp để biết kiểu CIM (ở ví dụ này FreeSpace / Size là uint649) rồi ép kiểu. Tưởng là int rồi ép, dẫn tới InvalidCastException, là vấp ngã kinh điển đầu tiên ở đây.
flowchart TB
accTitle: Cạm bẫy khi lấy thuộc tính và ép kiểu
accDescr: Thuộc tính System.Management trả về object qua indexer nên phải xác nhận kiểu CIM trong tài liệu lớp rồi mới ép, và ép vì tưởng là int thì thành InvalidCastException
idx["Lấy qua indexer"] --> obj["Trả về dưới dạng object"]
obj --> chk["Xác nhận kiểu CIM trong tài liệu"]
chk --> cast["Ép sang kiểu đúng"]
obj -.-> wrong["Ép vì tưởng là int"]
wrong -.-> ex["InvalidCastException"]
Hình 8: Thuộc tính trả về object nên xác nhận kiểu CIM rồi mới ép.
5.2. MI API: nền tảng CimSession
CimSession xử lý cục bộ và từ xa cùng một hình dạng. Nó gom liệt kê, truy vấn, gọi phương thức, đăng ký sự kiện, và cả biến thể bất đồng bộ.6
// NuGet: Microsoft.Management.Infrastructure (chỉ Windows)
using Microsoft.Management.Infrastructure;
// Cục bộ thì CimSession.Create(null), từ xa thì truyền tên máy
using CimSession session = CimSession.Create(null);
IEnumerable<CimInstance> disks = session.QueryInstances(
@"root\cimv2", "WQL",
"SELECT DeviceID, FreeSpace, Size FROM Win32_LogicalDisk WHERE DriveType = 3");
foreach (CimInstance disk in disks)
{
var deviceId = (string)disk.CimInstanceProperties["DeviceID"].Value;
var free = (ulong)disk.CimInstanceProperties["FreeSpace"].Value;
Console.WriteLine($"{deviceId} trống {free / 1024.0 / 1024 / 1024:F1} GB");
}
Vì nó làm việc với cùng CimInstance mà cmdlet CIM của PowerShell trả về, luồng phát triển “thử bằng PowerShell rồi chép sang C#” nối tự nhiên. Nếu bạn đang thiết kế chính sự phối hợp C# và PowerShell, cũng xem “Cách chạy PowerShell từ C# (CSharp) và nhận kết quả dưới dạng đối tượng”.
flowchart TB
accTitle: Thử bằng PowerShell rồi chép sang C#
accDescr: Cmdlet CIM của PowerShell và MI API của C# cùng làm việc với kiểu CimInstance nên luồng phát triển thử bằng PowerShell rồi chép sang C# nối tự nhiên
trial["Thử bằng PowerShell"] --> gci["Cmdlet CIM"]
impl["Triển khai thật bằng C#"] --> mi["MI API"]
gci --> ci["Cùng kiểu CimInstance"]
mi --> ci
ci -.-> flow["Chép sang nối tự nhiên"]
Hình 9: Cmdlet CIM và MI API cùng kiểu CimInstance nên từ thử tới triển khai thật nối được.
6. Công thức thực tế hay dùng
6.1. Bảng tra nhanh các lớp thường gặp
| Thông tin cần lấy | Lớp | Thuộc tính chính |
|---|---|---|
| Hãng / tên model | Win32_ComputerSystem |
Manufacturer, Model |
| Số serial thân máy | Win32_BIOS |
SerialNumber |
| Phiên bản OS / giờ khởi động | Win32_OperatingSystem |
Caption, Version, LastBootUpTime |
| Dung lượng đĩa trống | Win32_LogicalDisk |
DeviceID, FreeSpace, Size, DriveType9 |
| Trạng thái dịch vụ | Win32_Service |
Name, State, StartMode |
| Danh sách tiến trình | Win32_Process |
Name, ProcessId, CommandLine |
6.2. Công thức quản lý tài sản: số serial, tên model và dung lượng đĩa trống
# Thông tin mẫu mã và số sê-ri (để đối chiếu sổ tài sản PC)
$cs = Get-CimInstance -ClassName Win32_ComputerSystem -Property Manufacturer, Model
$bios = Get-CimInstance -ClassName Win32_BIOS -Property SerialNumber
[pscustomobject]@{
Manufacturer = $cs.Manufacturer
Model = $cs.Model
Serial = $bios.SerialNumber
}
# Dung lượng trống trên đĩa cục bộ (DriveType = 3)
Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType = 3" |
Select-Object DeviceID,
@{ Name = 'FreeGB'; Expression = { [math]::Round($_.FreeSpace / 1GB, 1) } },
@{ Name = 'SizeGB'; Expression = { [math]::Round($_.Size / 1GB, 1) } }
DriveType = 3 là giá trị “ổ đĩa cục bộ”, loại trừ ổ tháo lắp (2), ổ mạng (4) và ổ CD (5).9 Với giám sát, chỉ cần chạy tập lệnh này lần lượt từng máy chủ qua phiên CIM là có nền cho giám sát đĩa không agent.
flowchart TB
accTitle: Nền giám sát đĩa không agent
accDescr: Lọc DriveType 3 để loại ổ tháo lắp, ổ mạng và CD, chỉ còn ổ cục bộ, rồi chạy cùng tập lệnh qua phiên CIM lần lượt từng máy chủ thì có nền giám sát đĩa không agent
scr["Tập lệnh lấy dung lượng trống"] --> flt["Lọc DriveType = 3"]
flt -.-> exc["Loại trừ ổ tháo lắp v.v."]
scr --> ses["Qua phiên CIM"]
ses --> srvs["Chạy lần lượt từng máy chủ"]
srvs --> mon["Giám sát không agent"]
Hình 10: Chạy tập lệnh đã lọc ổ cục bộ qua phiên CIM lần lượt từng máy chủ là nền của giám sát.
6.3. Phát hiện tiến trình khởi chạy — đăng ký sự kiện
Thay vì “định kỳ lấy Win32_Process bằng polling rồi so sai khác”, hãy dùng đăng ký sự kiện. Cách đơn giản để bắt tiến trình khởi chạy là đăng ký Win32_ProcessStartTrace (lớp sự kiện của kernel trace provider, với thuộc tính như ProcessName / ProcessID / ParentProcessID8).
# Chạy từ phiên PowerShell với quyền quản trị
$action = {
$name = $Event.SourceEventArgs.NewEvent.ProcessName
$id = $Event.SourceEventArgs.NewEvent.ProcessID
Write-Host "Tiến trình khởi chạy: $name (PID=$id)"
}
Register-CimIndicationEvent -ClassName Win32_ProcessStartTrace `
-SourceIdentifier ProcessStarted -Action $action
Đăng ký còn hiệu lực suốt thời phiên PowerShell đã đăng ký còn sống, và -Action chạy mỗi khi một tiến trình khởi chạy. Đừng chạy luôn lệnh hủy ngay sau đó trong cùng một lô — làm vậy chỉ khiến đăng ký biến mất trước khi giám sát kịp bắt đầu. Chỉ chạy bước hủy khi bạn kết thúc giám sát.
# Khi kết thúc giám sát: hủy đăng ký
Unregister-Event -SourceIdentifier ProcessStarted
Register-CimIndicationEvent đăng ký theo tên lớp hoặc truy vấn sự kiện WQL, và khối script -Action chạy mỗi lần sự kiện đến.7 Đăng ký lớp này cần quyền quản trị viên.7 Ai nhận được sự kiện do mô tả bảo mật của lớp sự kiện kiểm soát, và người dùng thường không nâng quyền sẽ bị từ chối truy cập.8
sequenceDiagram
accTitle: Luồng đăng ký sự kiện khởi chạy tiến trình
accDescr: Phiên PowerShell với quyền quản trị viên dùng Register-CimIndicationEvent để đăng ký, mỗi khi tiến trình khởi chạy sự kiện đến và Action chạy, rồi Unregister-Event hủy khi kết thúc giám sát
participant ps as Phiên PowerShell
participant wmi as WMI
ps->>wmi: Đăng ký bằng Register-CimIndicationEvent
Note over ps: Chạy với quyền quản trị viên
wmi-->>ps: Sự kiện đến mỗi khi tiến trình khởi chạy
ps->>ps: Thực thi -Action
ps->>wmi: Hủy bằng Unregister-Event(khi kết thúc giám sát)
Hình 11: Đăng ký còn hiệu lực suốt thời phiên đã đăng ký còn sống, và hủy khi kết thúc giám sát.
Cách khác là sự kiện tạo instance chung (__InstanceCreationEvent), dùng được với lớp bất kỳ. Cơ chế này để WMI polling theo khoảng bạn chỉ định bằng WITHIN rồi biến sai khác thành sự kiện, nên bạn tự quyết đánh đổi giữa khoảng phát hiện và tải.
flowchart TB
accTitle: Hai cách đăng ký để phát hiện tiến trình khởi chạy
accDescr: Win32_ProcessStartTrace đăng ký lớp sự kiện của kernel trace provider, còn __InstanceCreationEvent chung để WMI polling theo khoảng WITHIN rồi biến sai khác thành sự kiện nên bạn tự quyết đánh đổi khoảng phát hiện và tải
goal["Phát hiện tiến trình khởi chạy"] --> t1["Win32_ProcessStartTrace"]
goal --> t2["__InstanceCreationEvent"]
t1 -.-> k1["Đăng ký kernel trace"]
t2 -.-> w1["Polling theo khoảng WITHIN"]
w1 -.-> tr["Đánh đổi khoảng và tải"]
Hình 12: Đăng ký lớp sự kiện chuyên biệt, hoặc dùng sự kiện tạo instance chung kèm khoảng polling.
# Giám sát thể hiện Win32_Process mới bằng polling mỗi 5 giây
$query = "SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process'"
Register-CimIndicationEvent -Query $query -SourceIdentifier ProcPoll -Action {
Write-Host "Khởi chạy: $($Event.SourceEventArgs.NewEvent.TargetInstance.Name)"
}
Trong C# (System.Management), ManagementEventWatcher đóng cùng vai trò.11
using System.Management;
// Từ tiến trình đang chạy với quyền quản trị
var watcher = new ManagementEventWatcher(
new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"));
watcher.EventArrived += (_, e) =>
{
var name = (string)e.NewEvent["ProcessName"];
var pid = (uint)e.NewEvent["ProcessID"];
Console.WriteLine($"Tiến trình khởi chạy: {name} (PID={pid})");
};
watcher.Start();
// Khi kết thúc giám sát đừng quên watcher.Stop() và Dispose
Nếu gắn vào giám sát thường trú, hãy đưa cả việc đăng ký lại khi đăng ký đứt (lúc dịch vụ khởi động lại hay khi lỗi) vào thiết kế. Luận thiết kế “kiểm tra và hiện trạng thái”, gồm giám sát thiết bị, nằm trong “Thực hành tốt nhất khi kiểm tra và hiển thị trạng thái thiết bị ngoài”.
stateDiagram-v2
accTitle: Vòng đời đăng ký của giám sát thường trú
accDescr: Giám sát thường trú có lúc trạng thái đang theo dõi bị đứt vì dịch vụ khởi động lại hay lỗi nên thiết kế phải gồm phát hiện đứt rồi đăng ký lại để trở về đang theo dõi
s1: Đang theo dõi
s2: Đăng ký đã đứt
s3: Đăng ký lại
[*] --> s1
s1 --> s2: Khởi động lại dịch vụ · lỗi
s2 --> s3
s3 --> s1
Hình 13: Giám sát thường trú cần thiết kế đăng ký lại khi đăng ký đứt để trở về đang theo dõi.
7. Cạm bẫy — hiệu năng, quyền, 64-bit, repository và ngày
7.1. SELECT * và polling quá dày
Truy vấn WMI là “provider dựng giá trị tại chỗ” — không miễn phí. Có hai anti-pattern kinh điển.
- Dùng
SELECT *theo thói quen. Lấy mọi thuộc tính mọi hàng củaWin32_Processlàm công việc của provider và lưu lượng mạng (khi từ xa) phình theo. Lọc hàng bằng-Filter, lọc cột bằng-Property, và dùng-KeyOnlynếu bạn chỉ cần khóa cho thao tác tiếp. Tất cả đều là phương tiện chính thức “để giảm kích thước đối tượng và lưu lượng mạng.”3 - Polling chu kỳ ngắn. Thiết kế kiểu “
Get-CimInstance Win32_Processmỗi giây” nên thay bằng đăng ký sự kiện ở mục 6.3. Dù phải dùng kiểu polling (WITHIN), hãy nới khoảng tới mức thật sự đủ cho yêu cầu.
Ngoài ra, lặp -ComputerName từng máy một với máy từ xa cũng lãng phí, vì phiên tạm được tạo cho mỗi lần truy vấn — hãy đổi sang tái sử dụng phiên CIM cho nhiều thao tác.3
flowchart TB
accTitle: Anti-pattern hiệu năng và chỗ thay
accDescr: Dùng SELECT * theo thói quen thì lọc hàng và cột bằng Filter và Property, chỉ cần khóa thì dùng KeyOnly, polling chu kỳ ngắn thì thay bằng đăng ký sự kiện, còn lặp ComputerName từng máy thì tái sử dụng phiên CIM
a1["Dùng SELECT * theo thói quen"] --> f1["Lọc bằng Filter và Property"]
f1 -.-> f2["Chỉ khóa thì dùng KeyOnly"]
a2["Polling chu kỳ ngắn"] --> f3["Thay bằng đăng ký sự kiện"]
a3["ComputerName từng máy một"] --> f4["Tái sử dụng phiên CIM"]
Hình 14: Lọc hàng · cột · khóa và thay bằng đăng ký sự kiện ngăn khoảng nửa vấn đề hiệu năng của WMI.
7.2. Quyền cho đăng ký sự kiện
Như mục 6.3, đăng ký họ Win32_ProcessStartTrace lấy quyền quản trị viên làm tiền đề.7 “Máy phát triển (chạy với quản trị viên) thì được, nhưng môi trường người dùng thường phía khách hàng thì giám sát không chạy” là tai nạn kinh điển, sánh với hộp thoại thông báo tường lửa. Nếu gắn giám sát vào ứng dụng nghiệp vụ chạy dưới người dùng thường, hãy cân nhắc tách phần giám sát ra dịch vụ Windows (chạy dưới LocalSystem chẳng hạn) và nối với ứng dụng chính bằng giao tiếp liên tiến trình.
flowchart TB
accTitle: Cấu hình giám sát trong môi trường người dùng thường
accDescr: Đăng ký lấy quyền quản trị viên làm tiền đề thì tách khỏi ứng dụng chính chạy dưới người dùng thường, đưa phần giám sát sang dịch vụ Windows chạy dưới LocalSystem v.v., rồi nối với ứng dụng chính bằng giao tiếp liên tiến trình
svcm["Dịch vụ Windows giám sát"] --> subm["Đăng ký start trace"]
svcm -.-> lsm["Chạy dưới LocalSystem v.v."]
appm["Ứng dụng chính(người dùng thường)"] ---|Giao tiếp liên tiến trình| svcm
Hình 15: Đăng ký cần quyền quản trị viên thì tách sang phía dịch vụ, rồi nối với ứng dụng chính bằng giao tiếp liên tiến trình.
7.3. 32-bit/64-bit và provider
Trên Windows 64-bit, một số provider tồn tại cả bản 32-bit lẫn 64-bit, và mặc định phía khớp bitness của ứng dụng gọi trả lời.12 Ví dụ điển hình là provider registry (StdRegProv) dưới root\default: đọc từ ứng dụng 32-bit trả về giá trị phía Wow6432Node (view 32-bit).12 Khi “giá trị registry đọc qua WMI không khớp những gì regedit hiện”, hãy nghi điều này trước. Nếu cần view phía kia, bạn có thể yêu cầu tường minh bằng cách đặt __ProviderArchitecture (và, nếu muốn ép, __RequiredArchitecture) trong ngữ cảnh kết nối.12 Toàn cảnh bài toán bitness cũng nằm trong “Gọi Win32 API an toàn từ C# — hướng dẫn thực hành P/Invoke”.
flowchart TB
accTitle: Chọn provider trên môi trường 64-bit
accDescr: Mặc định provider khớp bitness của ứng dụng gọi trả lời, truy vấn registry từ ứng dụng 32-bit nhận giá trị phía Wow6432Node, nhưng chỉ định __ProviderArchitecture thì yêu cầu tường minh được view phía kia
q1{"Bitness của bên gọi?"} -->|32bit| p32["Provider 32-bit trả lời"]
q1 -->|64bit| p64["Provider 64-bit trả lời"]
p32 -.-> wow["Registry lấy giá trị phía Wow6432Node"]
ctx["Chỉ định __ProviderArchitecture"] -.-> ov["Yêu cầu tường minh view phía kia"]
Hình 16: Mặc định phía khớp bitness của bên gọi trả lời, nên ứng dụng 32-bit đọc phía Wow6432Node.
7.4. Triệu chứng và xử lý khi WMI repository hỏng
Định nghĩa lớp WMI được lưu trong repository (không phải một tệp đơn — các tệp trong thư mục Repository cùng hoạt động như một cơ sở dữ liệu13). Khi nó mất nhất quán, lỗi kiểu “lớp lẽ ra tồn tại thì không tìm thấy” hay “không gian tên không hợp lệ” bắt đầu xuất hiện, dù phía ứng dụng chẳng đổi gì. Dùng winmgmt.exe để chẩn đoán và sửa.13
rem Kiểm tra tính nhất quán (kết quả inconsistent nghĩa là có lệch)
winmgmt /verifyrepository
rem Kiểm tra tính nhất quán và dựng lại nếu lệch (nội dung đọc được được gộp)
winmgmt /salvagerepository
Điều quan trọng là đừng lấy xóa hay khởi tạo repository làm bước đầu. Lỗi lộ qua WMI có thể bắt nguồn từ phần khác của OS, và chính Microsoft nói rõ rằng xóa repository làm phản ứng đầu “có thể gây tổn hại cho hệ thống hoặc ứng dụng đã cài.”13 Giữ thứ tự: kiểm tra bằng /verifyrepository, rồi sửa bằng /salvagerepository.
flowchart TB
accTitle: Quy trình tách nguyên nhân khi WMI repository mất nhất quán
accDescr: Khi có lỗi lớp không tìm thấy v.v. thì kiểm tra nhất quán bằng winmgmt verifyrepository, nếu mất nhất quán thì dựng lại bằng salvagerepository, và không lấy xóa hay khởi tạo repository làm bước đầu
sym["Lỗi lớp không tìm thấy v.v."] --> verify["winmgmt /verifyrepository"]
verify --> q1{"Kết quả là inconsistent?"}
q1 -->|Có| salvage["winmgmt /salvagerepository"]
q1 -->|Không| other["Nghi nguyên nhân ở phần khác của OS"]
salvage -.-> merge["Nội dung đọc được được merge"]
del["Xóa · khởi tạo repository"] -.-> ng["Không dùng làm bước đầu"]
Hình 17: Giữ thứ tự verify rồi salvage để sửa, đừng lấy xóa làm bước đầu.
7.5. Chuyển đổi định dạng ngày DMTF
Ngày WMI được lưu dạng chuỗi theo định dạng DMTF của đặc tả CIM: yyyymmddHHMMSS.mmmmmm±UUU (phần cuối là lệch so với UTC tính bằng phút — ví dụ 20260801100000.000000+540). Đừng cắt dán giá trị thô bằng xử lý chuỗi; hãy dùng API chuyển đổi.
- C# (System.Management):
ManagementDateTimeConvertercung cấp chuyển đổi giữa dạng DMTF vàDateTime/TimeSpan.11 - API họ CIM (Get-CimInstance / MI API): thuộc tính ngày trả về đã chuyển thành
DateTime, nên bạn không gặp bài toán này ngay từ đầu.(Get-CimInstance Win32_OperatingSystem).LastBootUpTimedùng trực tiếp nhưDateTimetrong tính toán.
flowchart TB
accTitle: Cách xử lý định dạng ngày DMTF
accDescr: Ngày WMI được lưu dạng chuỗi DMTF, nếu đọc giá trị thô bằng System.Management thì chuyển bằng ManagementDateTimeConverter, còn API họ CIM thì đã trả DateTime sẵn nên không tự cắt dán chuỗi
dmtf["Chuỗi dạng DMTF"] --> q1{"Lấy bằng API nào?"}
q1 -->|System.Management| conv["ManagementDateTimeConverter"]
q1 -->|API họ CIM| done["Đã chuyển thành DateTime"]
conv --> dtv["Chuyển sang DateTime / TimeSpan"]
cut["Tự cắt dán chuỗi"] -.-> ng["Không dùng"]
Hình 18: Để API chuyển đổi lo chuỗi DMTF, còn API họ CIM thì dùng DateTime đã chuyển sẵn.
8. Khi không nên dùng WMI — bảng quyết định công cụ
WMI xuất sắc như “cửa đọc thống nhất”, nhưng không phải lúc nào cũng tối ưu. Đây là thước đo thực tế để chọn công cụ.
| Việc muốn làm | Công cụ đúng | Vì sao không chọn WMI |
|---|---|---|
| Lấy thông tin phần cứng và cấu hình OS, truy vấn từ xa không agent | WMI/CIM | Đây đúng là sân nhà của WMI — thống nhất hơn việc gọi từng API chuyên biệt |
| Đọc ghi thiết lập của chính ứng dụng | Đọc registry trực tiếp (Microsoft.Win32.Registry) hoặc tệp cấu hình |
Thao tác registry qua WMI là đường vòng, và còn mang theo bài toán bitness ở mục 7.3 |
| Giám sát hiệu năng liên tục tần suất cao như CPU | Bộ đếm hiệu năng (System.Diagnostics.PerformanceCounter, v.v.) |
Counter được làm đúng cho việc này. Polling WMI chu kỳ ngắn thua cả tải lẫn độ chính xác |
| Gọi chức năng OS một lần, hoặc xử lý cần độ trễ thấp | Win32 API (P/Invoke) | WMI mang phí đi qua COM/provider |
| Liệt kê và thao tác tiến trình cục bộ khi quyền của chính tiến trình đủ | System.Diagnostics.Process |
Tự đủ trong thư viện chuẩn, ít phụ thuộc hơn |
| Cấu hình tính năng quản lý Windows như tường lửa hay mạng | Cmdlet CIM chuyên biệt như Get-NetFirewallRule |
Bộ cmdlet xây và duy trì cho một mục đích cụ thể chính xác và an toàn hơn việc lần lớp WMI thô |
| Phát hiện thay đổi tệp hay thư mục | FileSystemWatcher |
Đừng đưa WMI vào vùng đã có API chuyên biệt |
Trục phán đơn giản: ở vùng đã có cơ chế chuyên biệt thì dùng cơ chế chuyên biệt, và dành WMI/CIM cho truy vấn xuyên suốt cùng truy vấn từ xa. Ví dụ Get-NetFirewallRule ở hàng cuối, bên trong, là bộ cmdlet dựng trên CIM — dạng “hưởng lợi ích của WMI/CIM mà không đụng trực tiếp.”
flowchart TB
accTitle: Trục phán khi chọn công cụ
accDescr: Ở vùng đã có cơ chế chuyên biệt thì dùng cơ chế chuyên biệt, còn truy vấn xuyên suốt hay truy vấn từ xa ở vùng không có thì dùng WMI và CIM, và cmdlet CIM chuyên biệt là dạng chỉ hưởng lợi ích mà không đụng WMI và CIM trực tiếp
q1{"Có cơ chế chuyên biệt?"} -->|Có| ded["Dùng cơ chế chuyên biệt"]
q1 -->|Không| wmi["Dùng WMI / CIM"]
wmi -.-> use["Truy vấn xuyên suốt · truy vấn từ xa"]
cmd["Cmdlet CIM chuyên biệt"] -.-> ben["Dạng chỉ hưởng lợi ích"]
Hình 19: Ở vùng đã có cơ chế chuyên biệt thì dùng chuyên biệt, và dành WMI/CIM cho truy vấn xuyên suốt cùng truy vấn từ xa.
9. Tóm tắt
- CIM là chuẩn ngành của DMTF, WMI là triển khai của Microsoft cho chuẩn đó. Cả cmdlet CIM của PowerShell lẫn MI API của C# đều là cửa vào thế hệ hiện hành bám chuẩn này.
- Trên PowerShell, Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent là hiện hành. Get-WmiObject và các cmdlet WMI khác không tồn tại trên PowerShell 7, nên tập lệnh mới viết phía CIM ngay cả khi nhắm 5.1.
- Truy vấn từ xa mặc định dùng WSMan (WinRM), và nhiều thao tác thì tái sử dụng phiên CIM. Với máy chưa cấu hình WinRM, DCOM là đường thoát có sẵn.
- Trên C#, chọn giữa System.Management (tiện, hướng cục bộ) và Microsoft.Management.Infrastructure (hướng từ xa và giám sát, cùng hệ kiểu với cmdlet CIM). Cả hai đều là gói NuGet chỉ dành cho Windows.
- Giám sát tiến trình bằng đăng ký sự kiện, không polling. Đăng ký Win32_ProcessStartTrace cần quyền quản trị viên.
- Tránh SELECT * và polling chu kỳ ngắn — lọc bằng -Filter / -Property / -KeyOnly. Nhớ rằng truy vấn từ tiến trình 32-bit được provider 32-bit phục vụ, ngày DMTF cần API chuyển đổi, và repository hỏng thì xử lý theo thứ tự verify → salvage, không xóa.
- Đừng đưa WMI vào vùng đã có cơ chế chuyên biệt (thiết lập, bộ đếm hiệu năng, gọi API một lần) — dành WMI/CIM cho truy vấn xuyên suốt và truy vấn từ xa. Một câu đó tóm chỗ đứng của nó.
Bài viết liên quan
- Cách chạy PowerShell từ C# (CSharp) và nhận kết quả dưới dạng đối tượng
- Công thức lệnh PowerShell thực dụng — thêm các công cụ nhỏ dùng hàng ngày
- Khác biệt giữa Windows PowerShell 5.1 và PowerShell 7 — hướng dẫn thực hành di chuyển tập lệnh nội bộ
- Thực hành tốt nhất khi kiểm tra và hiển thị trạng thái thiết bị ngoài - thiết kế không dừng ở chỉ “đã kết nối”
- Gọi Win32 API an toàn từ C# — hướng dẫn thực hành P/Invoke (DllImport / LibraryImport / CsWin32)
- TPM trên Windows là gì? — minh họa “két không để khóa ra ngoài” và measured boot
Lĩnh vực tư vấn liên quan
KomuraSoft LLC đảm nhận việc gắn lấy thông tin phần cứng, giám sát tiến trình và truy vấn PC từ xa bằng WMI/CIM vào ứng dụng nghiệp vụ, di chuyển tập lệnh nội bộ dựa trên Get-WmiObject sang cmdlet CIM, và điều tra loại sự cố “máy phát triển chạy được nhưng phía khách hàng báo lỗi quyền.” Chúng tôi hỗ trợ xuyên suốt từ thử bằng PowerShell đến triển khai thật bằng C#.
- Phát triển ứng dụng Windows
- Điều tra lỗi và nguyên nhân
- Tư vấn kỹ thuật và đánh giá thiết kế
- Liên hệ với chúng tôi
Liên kết tham khảo
-
Microsoft Learn, About WMI. Về việc WMI là triển khai của Microsoft cho WBEM (sáng kiến ngành phát triển công nghệ chuẩn để truy cập thông tin quản lý trong môi trường doanh nghiệp), biểu diễn đối tượng quản lý bằng chuẩn ngành CIM (Common Information Model) do DMTF (Distributed Management Task Force) phát triển và duy trì; về MI (Windows Management Infrastructure) thế hệ kế tương thích hoàn toàn với WMI cũ; và về kết nối WMI từ xa dùng DCOM, với WinRM dựa trên WS-Management như lựa chọn thay. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Differences between Windows PowerShell 5.1 and PowerShell 7.x. Về việc các cmdlet WMI v1 (Register-WmiEvent / Set-WmiInstance / Invoke-WmiMethod / Get-WmiObject / Remove-WmiObject) đã bị xóa khỏi PowerShell, và các cmdlet module CimCmdlets (WMI v2) cung cấp cùng chức năng với tính năng mới và cú pháp thiết kế lại. ↩ ↩2
-
Microsoft Learn, Get-CimInstance (CimCmdlets). Về việc không chỉ định ComputerName lẫn CimSession thì kết nối tới WMI cục bộ qua phiên COM, còn khi chỉ định -ComputerName thì tạo phiên tạm qua giao thức WsMan; về việc kết nối qua phiên CIM được khuyến nghị về hiệu năng khi thực hiện nhiều thao tác trên cùng máy; về -Filter là mệnh đề where WQL/CQL không gồm từ khóa WHERE; về -Property và -KeyOnly giảm kích thước đối tượng và lưu lượng mạng; về không gian tên mặc định là root/CIMV2 và ngôn ngữ truy vấn mặc định (-QueryDialect) là WQL; về đầu ra là Microsoft.Management.Infrastructure.CimInstance; về ví dụ gọi GetOwner kết hợp Invoke-CimMethod; và về cmdlet chỉ dành cho Windows. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10
-
Microsoft Learn, New-CimSessionOption (CimCmdlets). Về việc tùy chọn phiên CIM có hai bộ tham số, cho WsMan và cho DCOM; về -Protocol nhận Dcom / Default / Wsman; về ví dụ đưa tùy chọn tạo bằng New-CimSessionOption -Protocol Dcom vào -SessionOption của New-CimSession để tạo phiên CIM DCOM; và về mức giả danh mặc định của phiên DCOM là Impersonate. ↩ ↩2
-
Microsoft Learn, ManagementObjectSearcher Class (System.Management). Về đây là lớp cửa vào phổ biến nhất để lấy thông tin quản lý, lấy bộ sưu tập đối tượng quản lý dựa trên truy vấn WQL chỉ định; về nó nhận ObjectQuery và ManagementScope (không gian tên WMI) rồi trả ManagementObjectCollection qua Get(); và về System.Management.dll được cung cấp như gói NuGet System.Management. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, CimSession Class (Microsoft.Management.Infrastructure). Về Microsoft.Management.Infrastructure.dll được cung cấp như gói NuGet Microsoft.Management.Infrastructure; về tạo phiên qua Create(computerName); về thực thi truy vấn qua QueryInstances(namespace, queryDialect, query); và về nó cung cấp EnumerateInstances / GetInstance / InvokeMethod / Subscribe cùng biến thể bất đồng bộ (*Async) của từng cái, đồng thời triển khai IDisposable. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Register-CimIndicationEvent (CimCmdlets). Về việc đăng ký indication (sự kiện) theo tên lớp hoặc biểu thức truy vấn và đặt tên đăng ký bằng -SourceIdentifier; về ví dụ đăng ký Win32_ProcessStartTrace, kèm ghi chú rằng cần chạy PowerShell với quyền quản trị viên; về ví dụ tham chiếu ProcessName / ProcessId từ $Event.SourceEventArgs.NewEvent trong khối script -Action; về kết nối qua phiên tạm WsMan khi chỉ định -ComputerName, và cục bộ qua COM khi không chỉ định; và về dùng Unregister-Event để hủy đăng ký. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Win32_ProcessStartTrace class. Về đây là lớp sự kiện báo một tiến trình mới khởi chạy, với thuộc tính gồm ProcessName / ProcessID / ParentProcessID / SessionID / Sid; về thuộc tính SECURITY_DESCRIPTOR là mô tả mà event provider dùng để quyết người dùng nào nhận được sự kiện; và về không gian tên là Root\CIMV2, do kernel trace provider (Krnlprov.dll) cung cấp. ↩ ↩2 ↩3
-
Microsoft Learn, Win32_LogicalDisk class. Về Win32_LogicalDisk là lớp dẫn xuất từ CIM_LogicalDisk biểu diễn thiết bị lưu trữ cục bộ; về giá trị DriveType (2 = ổ tháo lắp, 3 = ổ đĩa cục bộ, 4 = ổ mạng, 5 = CD, v.v.); về FreeSpace / Size là giá trị byte uint64; về DeviceID là khóa; và về ví dụ truy vấn VBScript / C# lọc DriveType = 3. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Installation and configuration for Windows Remote Management. Về việc mặc định listener WinRM chưa được cấu hình nên không gửi nhận được thông điệp WS-Management; về winrm quickconfig đặt dịch vụ tự khởi động, cấu hình listener HTTP/HTTPS, và đăng ký ngoại lệ tường lửa; về cổng mặc định của WinRM 2.0 là HTTP 5985 / HTTPS 5986; về đặt TrustedHosts hẹp hết mức khi không thiết lập được xác thực lẫn nhau (Kerberos), như trong workgroup; và về mô tả bảo mật mặc định (RootSDDL) kiểm soát truy cập từ xa tới listener, cùng cấu hình thêm cần thiết để cho phép người dùng không phải quản trị viên dùng plugin WMI. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, System.Management Namespace. Về đây là không gian tên truy vấn hạ tầng WMI qua họ lớp ManagementObjectSearcher, và xử lý đăng ký sự kiện qua ManagementEventWatcher; về WqlEventQuery biểu diễn truy vấn sự kiện dạng WQL; và về ManagementDateTimeConverter cung cấp phương thức chuyển giữa biểu diễn ngày/giờ và khoảng thời gian DMTF với DateTime / TimeSpan của CLR. ↩ ↩2 ↩3
-
Microsoft Learn, Requesting WMI Data on a 64-bit Platform. Về việc, khi provider tồn tại cả bản 32-bit lẫn 64-bit, mặc định provider 32-bit trả lời ứng dụng 32-bit (gồm script) và provider 64-bit trả lời ứng dụng 64-bit; về việc có thể yêu cầu hoặc ép phiên bản provider không mặc định qua __ProviderArchitecture (32 hoặc 64) và __RequiredArchitecture của ngữ cảnh (WBEM_E_PROVIDER_LOAD_FAILURE xảy ra nếu ép phiên bản không có); và về ví dụ provider registry trong đó máy khách 32-bit nhận dữ liệu phía HKLM\SOFTWARE\Wow6432Node. ↩ ↩2 ↩3
-
Microsoft Learn, winmgmt. Về /verifyrepository của winmgmt.exe kiểm tra nhất quán WMI repository; về /salvagerepository kiểm tra nhất quán rồi, nếu phát hiện mất nhất quán, dựng lại repository đồng thời merge nội dung đọc được; về /resetrepository khôi phục repository về trạng thái lúc cài OS lần đầu; về repository hoạt động như cơ sở dữ liệu gồm các tệp trong thư mục Repository; và về lỗi lộ qua WMI đôi khi bắt nguồn từ phần khác của OS, nên tránh xóa repository làm phản ứng đầu vì có thể gây tổn hại cho hệ thống hoặc ứng dụng đã cài. ↩ ↩2 ↩3
Bài viết liên quan
Các bài viết gần đây có cùng thẻ để tìm hiểu sâu hơn những chủ đề lân cận.
Thực hành tốt nhất về đa luồng: ấn bản .NET — Những gì cần quyết trước khi thêm luồng
Tổng hợp các định thức thiết kế ngăn «thêm luồng rồi thỉnh thoảng crash hoặc treo» cho .NET/C#. Bài viết đi từ việc dựa trên Task thay vì...
Named pipe trong thực tiễn — IPC chuẩn của Windows, từ thiết kế đến bảo mật
Hướng dẫn thực tiễn về named pipe, cơ chế giao tiếp giữa các tiến trình chuẩn của Windows. Bài viết sắp xếp, từ nguồn gốc, việc chọn giữa...
Ứng dụng hỏng khi thức dậy từ ngủ — Sự kiện nguồn Windows và cách viết ứng dụng nghiệp vụ sống sót
Bạn mở laptop và kết nối của ứng dụng nghiệp vụ đã chết — nguyên nhân là thiết kế chưa tính đến ngủ. Bài viết này trình bày luồng thông b...
API thread pool Win32 — Đồng thời mà không tạo luồng, với CreateThreadpoolWork
Bạn đang rải các lời gọi CreateThread khắp mã native? Bài viết này giải thích API thread pool Win32 được thiết kế lại từ Vista — bốn đối ...
DllMain và Loader Lock — Lý do thật sự bạn bị bảo "đừng làm gì trong khởi tạo DLL"
Vì sao bạn không được gọi LoadLibrary hoặc đồng bộ với luồng khác từ DllMain. Dựa trên nguồn gốc, bài viết giải thích cách loader lock tu...
Chủ đề liên quan
Các trang này đặt chủ đề trong bối cảnh rộng hơn của dịch vụ và quyết định.
Chủ đề kỹ thuật Windows
Cổng vào phát triển Windows, điều tra lỗi và khai thác tài sản hiện có.
Dịch vụ liên quan đến chủ đề này
Bài viết liên quan trực tiếp đến các dịch vụ sau.
Phát triển ứng dụng Windows
Ứng dụng nghiệp vụ, tích hợp thiết bị và công cụ liên lạc, từ yêu cầu đến phát triển.
Câu hỏi thường gặp
Các câu hỏi thường gặp khi tư vấn về chủ đề của bài viết.
- WMI và CIM khác nhau thế nào?
- CIM là "mô hình chuẩn ngành để biểu diễn đối tượng quản lý như hệ thống và thiết bị", do DMTF (Distributed Management Task Force) soạn thảo và duy trì. WMI là triển khai của Microsoft cho WBEM — sáng kiến dùng chuẩn đó — và được gắn sẵn trong Windows. Nói cách khác, CIM là đặc tả, WMI là triển khai trên Windows. Get-CimInstance của PowerShell và Microsoft.Management.Infrastructure của C# tự gọi là "CIM" vì chúng là API bám chuẩn này, nhưng điểm kết nối vẫn là cùng một nền tảng WMI. Với phát triển hàng ngày, hiểu là "truy vấn lớp WMI (Win32_* và tương tự) qua API họ CIM" là đủ cho thực tế.
- Get-WmiObject không còn dùng được nữa sao?
- Trên Windows PowerShell 5.1 nó vẫn chạy, nhưng từ PowerShell 6 trở đi (gồm PowerShell 7 hiện hành), các cmdlet WMI v1 — Get-WmiObject, Invoke-WmiMethod, Register-WmiEvent, Set-WmiInstance và Remove-WmiObject — đã bị xóa và không chạy được. Cùng chức năng đó nằm trong module CimCmdlets (Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent, v.v.). Tập lệnh viết mới nên viết bằng cmdlet CIM ngay cả khi sẽ chạy trên 5.1. Làm vậy thì khi di chuyển sang PowerShell 7 bạn không phải viết lại phần WMI.
- Từ C# nên dùng System.Management hay Microsoft.Management.Infrastructure để truy cập WMI?
- Cả hai đều chỉ dành cho Windows, và từ .NET hiện hành bạn đưa chúng vào như gói NuGet. System.Management là API cổ điển, dùng được chỉ bằng cách đưa WQL vào ManagementObjectSearcher, và đủ nếu bạn chủ yếu lấy thông tin cục bộ. Nó cũng gồm ManagementDateTimeConverter để chuyển ngày DMTF. Còn Microsoft.Management.Infrastructure (MI API) chia sẻ cùng hệ kiểu (CimSession / CimInstance) với cmdlet CIM của PowerShell, và xử lý thống nhất truy vấn từ xa qua WSMan, biến thể phương thức bất đồng bộ, cùng đăng ký sự kiện (Subscribe). Nếu bạn gắn truy vấn PC từ xa và giám sát vào sản phẩm thật, chọn MI API là hợp lý.
- Get-CimInstance không kết nối được tới PC từ xa. Nên kiểm tra gì?
- Trước hết kiểm tra WinRM đã được cấu hình trên máy đích chưa. Thao tác CIM có -ComputerName tạo phiên tạm qua giao thức WSMan (WinRM), nên giả định dịch vụ WinRM và listener đang chạy trên đích. winrm quickconfig thực hiện cấu hình mặc định (khởi động dịch vụ, tạo listener, đăng ký ngoại lệ tường lửa). Cổng mặc định là 5985 cho HTTP và 5986 cho HTTPS, nên cũng kiểm tra tường lửa trên đường đi. Trong môi trường workgroup, xác thực lẫn nhau qua Kerberos không dùng được, nên bạn có thể cần đăng ký đích vào TrustedHosts phía máy khách. Với máy tuyệt đối không cấu hình được WinRM, bạn có thể kết nối qua DCOM bằng tùy chọn tạo từ New-CimSessionOption -Protocol Dcom.
- Vì sao ngày WMI trả về dạng "20260801100000.000000+540"?
- Ngày WMI được lưu theo chuỗi do đặc tả CIM của DMTF quy định (yyyymmddHHMMSS.mmmmmm±UUU, phần cuối là lệch so với UTC tính bằng phút). Nếu bạn đọc giá trị thô bằng Get-WmiObject cũ hoặc System.Management, chuỗi này trả về nguyên vậy. Trong C# (System.Management), ManagementDateTimeConverter cung cấp phương thức chuyển giữa dạng DMTF và DateTime / TimeSpan, nên dùng chúng thay vì tự cắt dán chuỗi. Lưu ý rằng khi lấy dữ liệu qua API họ CIM như Get-CimInstance, thuộc tính ngày đã được chuyển thành DateTime sẵn, nên bạn không gặp bài toán này ngay từ đầu.