ใช้ WMI/CIM จาก C# และ PowerShell — คู่มือปฏิบัติการดึงข้อมูลฮาร์ดแวร์ เฝ้าโพรเซส และสอบถามระยะไกล
· Go Komura · Windows, C#, .NET, PowerShell, WMI, CIM, แอปธุรกิจ, การพัฒนา Windows
«อยากโชว์หมายเลขซีเรียลกับชื่อรุ่นของพีซีบนหน้าจอแอปธุรกิจ» «อยากเฝ้าพื้นที่ว่างดิสก์ของเซิร์ฟเวอร์แล้วเตือน» «อยากตรวจจับว่าโพรเซสหนึ่งเริ่มทำงาน» «อยากสอบถามสถานะพีซีที่อยู่ที่อื่นรวมในที่เดียว» — ข้อกำหนดแบบนี้เป็นของประจำตอนสร้างแอปธุรกิจและเครื่องมือจัดการบน Windows และคำตอบมาตรฐานคือ WMI (Windows Management Instrumentation) หรือในชื่อมาตรฐานคือ CIM (Common Information Model)
flowchart TB
accTitle: ข้อกำหนดประจำกับ WMI/CIM
accDescr: คำตอบมาตรฐานต่อข้อกำหนดประจำของแอปธุรกิจ คือการแสดงหมายเลขซีเรียลกับชื่อรุ่น การเฝ้าพื้นที่ว่างดิสก์ การตรวจจับการเริ่มโพรเซส และการสอบถามพีซีระยะไกล คือ WMI หรือในชื่อมาตรฐานคือ CIM
r1["หมายเลขซีเรียล・ชื่อรุ่น"] --> ans["WMI(ชื่อมาตรฐานคือ CIM)"]
r2["เฝ้าพื้นที่ว่างดิสก์"] --> ans
r3["ตรวจจับการเริ่มโพรเซส"] --> ans
r4["สอบถามพีซีระยะไกล"] --> ans
ภาพ 1: คำตอบมาตรฐานต่อสี่ข้อกำหนดประจำของแอปธุรกิจคือ WMI/CIM
สิ่งที่ยากคือข้อมูลรอบ WMI ปนของเก่ากับของใหม่ ค้นแล้วบทความสิบปีที่ใช้ Get-WmiObject นั่งข้างบทความที่ใช้ Get-CimInstance และฝั่ง C# ก็มีสองสายคือ System.Management กับ Microsoft.Management.Infrastructure แยกได้ยากว่าอันใดคือวิธีเขียนปัจจุบัน และอันใดคือ «ยังรันได้แต่ไม่เลือกสำหรับโค้ดใหม่» จริง ๆ แล้ว Get-WmiObject ไม่มีใน PowerShell 7 และเรื่องนี้โผล่ทันทีตอนย้ายสคริปต์ในองค์กรที่เขียนไว้สำหรับ 5.1
บทความนี้มุ่งนักพัฒนา C#/PowerShell ที่ใส่การดึงข้อมูลฮาร์ดแวร์ การเฝ้าโพรเซส และการสอบถามพีซีระยะไกลในแอปธุรกิจ จัดจากความเข้าใจโครงสร้าง WMI/CIM เท่าที่จำเป็น ผ่าน cmdlet CIM ของ PowerShell API สองสายของ C# สูตรที่ใช้บ่อย กับดักเรื่องประสิทธิภาพ สิทธิ์ และ 64bit จนถึงเกณฑ์ตัดสิน «ตอนที่ไม่ควรใช้ WMI» ทั้งหมดยึดแหล่งปฐมภูมิ ณ สิงหาคม 2026
1. สรุปก่อนเลย
- CIM คือโมเดลมาตรฐานอุตสาหกรรมของข้อมูลการจัดการที่ DMTF กำหนด และ WMI คืออิมพลีเมนต์ของ Microsoft สำหรับมาตรฐานนั้น API ตระกูล «CIM» ใน PowerShell และ C# คือ API รุ่นปัจจุบันที่ตามมาตรฐานนี้ และเชื่อมไปโครงสร้าง WMI เดียวกัน1
- ใน PowerShell รุ่นปัจจุบันคือ cmdlet CIM (Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent) cmdlet WMI เก่า (Get-WmiObject และอีกสี่ตัว) ถูกลบตั้งแต่ PowerShell 6 เป็นต้นไป และไม่รันใน PowerShell 72
- เนมสเปซเริ่มต้นคือ root/CIMV2 และการสอบถามประจำโดยพื้นฐานคือกรองคลาส Win32_* ที่นั่นด้วย WQL3
- การสอบถามระยะไกลใช้ WSMan (WinRM) เป็นค่าเริ่มต้น การระบุ
-ComputerNameสร้างเซสชันชั่วคราว WSMan หากจะสอบถามปลายทางเดิมหลายครั้ง การใช้เซสชัน CIM (New-CimSession) ซ้ำคือวิธีประจำด้านประสิทธิภาพ และสำหรับปลายทางเก่าที่ตั้ง WinRM ไม่ได้ มีตัวเลือกโปรโตคอล DCOM34 - C# มีสองสาย: System.Management (ManagementObjectSearcher) และ Microsoft.Management.Infrastructure (CimSession) ทั้งคู่เป็น Windows เท่านั้น และบน .NET ปัจจุบันนำเข้าจาก NuGet หากจะใส่การสอบถามระยะไกลหรือการเฝ้าลงผลิตภัณฑ์จริง MI API ที่ใช้ระบบชนิดเดียวกับ cmdlet CIM เหมาะกว่า56
- ตรวจจับการเริ่มโพรเซสด้วยการสมัครรับเหตุการณ์ อย่าใช้พอลลิง การสมัครรับ
Win32_ProcessStartTraceต้องรันด้วยสิทธิ์ผู้ดูแล78 - อย่าใช้
SELECT *ตามเคย การจำกัดข้อมูลที่ส่งด้วย-Filter/-Property/-KeyOnlyป้องกันปัญหาประสิทธิภาพของ WMI ได้ประมาณครึ่งหนึ่ง3 - WMI ไม่ใช่คำตอบครอบจักรวาล สำหรับการเฝ้าประสิทธิภาพความถี่สูง การอ่านเขียนการตั้งค่าของแอปเอง หรือการเรียกฟังก์ชัน OS ครั้งเดียว เคาน์เตอร์ประสิทธิภาพ รีจิสทรี Win32 API หรือ cmdlet เฉพาะงานเหมาะกว่า (ตารางตัดสินในบทที่ 8)
2. WMI/CIM คืออะไร — มาตรฐานกับการอิมพลีเมนต์ เนมสเปซ คลาส และ WQL
ก่อนอื่นจัดความสัมพันธ์ของคำศัพท์ให้จบในรอบเดียว
| คำ | คืออะไร |
|---|---|
| CIM (Common Information Model) | โมเดลมาตรฐานอุตสาหกรรม สำหรับแสดงเป้าการจัดการอย่างระบบ แอป เครือข่าย และอุปกรณ์ DMTF (Distributed Management Task Force) เป็นผู้กำหนดและดูแล1 |
| WBEM (Web-Based Enterprise Management) | โครงการอุตสาหกรรมที่สร้างเทคโนโลยีมาตรฐานสำหรับเข้าถึงข้อมูลการจัดการในสภาพแวดล้อมองค์กร1 |
| WMI | อิมพลีเมนต์ของ Microsoft สำหรับ WBEM แสดงเป้าการจัดการด้วยมาตรฐาน CIM และฝังอยู่ใน Windows1 |
| MI (Windows Management Infrastructure) | รุ่นถัดไปของ WMI เข้ากันได้เต็มกับ WMI เดิม และโปรไวด์เดอร์ใหม่ส่วนใหญ่เขียนด้วย MI1 |
flowchart TB
accTitle: ความสัมพันธ์ระหว่างมาตรฐาน CIM กับอิมพลีเมนต์ WMI
accDescr: มาตรฐาน CIM ที่ DMTF กำหนดและดูแลถูกใช้ในกรอบโครงการ WBEM อิมพลีเมนต์ของ Microsoft คือ WMI รุ่นถัดไป MI เข้ากันได้เต็มกับ WMI เดิม และปลายทางของ API ตระกูล CIM คือโครงสร้าง WMI เดียวกัน
dmtf["DMTF กำหนดและดูแล"] --> cim["CIM(โมเดลมาตรฐานอุตสาหกรรม)"]
wbem["WBEM(โครงการอุตสาหกรรม)"] --> wmi["WMI(อิมพลีเมนต์ของ Microsoft)"]
cim --> wmi
wmi -.-> mi["MI(รุ่นถัดไป・เข้ากันได้เต็ม)"]
api["API ตระกูล CIM(PowerShell / C#)"] --> wmi
ภาพ 2: CIM คือข้อกำหนด WMI คืออิมพลีเมนต์บน Windows ปลายทางของ API ตระกูล CIM คือโครงสร้าง WMI เดียวกัน
โครงสร้างที่นักพัฒนาควรจับมีสี่ชิ้น
- เนมสเปซ (namespace): ลำดับชั้นที่มัดคลาสไว้ด้วยกัน สำหรับการสอบถามประจำคุณใช้ root/CIMV2 เกือบทุกครั้ง ซึ่งเป็นค่าเริ่มต้นของ cmdlet CIM ด้วย3 อื่น ๆ มี
root\default(โปรไวด์เดอร์รีจิสทรี เป็นต้น) - คลาส: ชนิดที่แทนเป้าการจัดการ เช่น
Win32_ComputerSystem(ตัวคอมพิวเตอร์)Win32_LogicalDisk(ไดรฟ์เชิงตรรกะ) หรือWin32_Process(โพรเซส) คลาสเฉพาะ Windows ที่สืบทอดจากคลาสมาตรฐาน CIM (เช่นCIM_LogicalDisk) ใช้คำนำหน้าWin32_9 - โปรไวด์เดอร์: คอมโพเนนต์ที่ให้เนื้อหาของคลาส เมื่อคุณสอบถาม โปรไวด์เดอร์ถาม OS ณ เวลานั้นแล้วสร้างค่า
- WQL: ภาษาสอบถามคล้าย SQL ดังใน
SELECT Name, State FROM Win32_Service WHERE StartMode = 'Auto'มันมองคลาสเป็นตารางแล้วกรอง WQL ยังเป็นภาษาสอบถามเริ่มต้นของ cmdlet CIM3
«อ่านข้อมูล OS และฮาร์ดแวร์ผ่านชุดคลาสและภาษาคิวรีเดียวกัน» — นั่นคือคุณค่าที่ WMI ให้ ในทางกลับกัน การเขียนและการควบคุมจำกัดอยู่ที่คลาสส่วนที่มีเมธอดเรียกผ่าน Invoke-CimMethod ได้ ไม่ใช่กลไกที่ทำได้ทุกอย่าง
flowchart TB
accTitle: โครงสร้างการสอบถาม WMI
accDescr: คิวรี WQL มุ่งไปคลาส Win32_* ในเนมสเปซ root/CIMV2 โปรไวด์เดอร์ที่ให้เนื้อหาของคลาสถาม OS ณ เวลานั้นแล้วสร้างค่า แล้วคืนผลลัพธ์
wql["สอบถามด้วย WQL"] --> ns["เนมสเปซ root/CIMV2"]
ns --> cls["คลาส Win32_*"]
cls --> prov["โปรไวด์เดอร์"]
prov --> osq["ถาม OS ณ เวลานั้น"]
osq --> res["คืนผลลัพธ์"]
ภาพ 3: การสอบถามเดินจากเนมสเปซ ไปคลาส ไปโปรไวด์เดอร์ ตามลำดับ และค่าถูกสร้าง ณ เวลานั้น
3. การใช้จาก PowerShell — cmdlet CIM คือรุ่นปัจจุบัน cmdlet WMI ถูกลบแล้ว
3.1. พื้นฐานคือ Get-CimInstance
# ระบุคลาส (เนมสเปซเริ่มต้น root/CIMV2)
Get-CimInstance -ClassName Win32_OperatingSystem
# เขียนเฉพาะเนื้อหาประโยค WHERE ใน -Filter (อย่าเขียนคำว่า WHERE เอง)
Get-CimInstance -ClassName Win32_Service -Filter "StartMode = 'Auto' AND State <> 'Running'"
# ดึงเฉพาะคุณสมบัติที่ต้องใช้ เพื่อลดปริมาณที่ส่ง
Get-CimInstance -ClassName Win32_Process -Property Name, ProcessId, CreationDate
# ถ้าจะเขียน WQL ตามที่เป็น ใช้ -Query
Get-CimInstance -Query "SELECT * FROM Win32_Process WHERE Name LIKE 'p%'"
-Filter คือประโยค WHERE ของ WQL ตามที่เป็น และ -Property จำกัดคอลัมน์ที่ดึง3 ค่าที่คืนคืออ็อบเจกต์ CimInstance และคุณสมบัติวันที่ (เช่น CreationDate หรือ LastBootUpTime) กลับมาแปลงเป็น DateTime แล้ว ไม่เหมือน Get-WmiObject เก่า อ็อบเจกต์ที่ดึงมาไม่มีเมธอดโดยตรง ดังนั้นการเรียกเมธอดทำโดยส่งต่อไป Invoke-CimMethod
flowchart TB
accTitle: การเรียกเมธอดของ CimInstance
accDescr: CimInstance ที่ Get-CimInstance คืนมีคุณสมบัติวันที่แปลงเป็น DateTime แล้ว แต่ไม่มีเมธอดโดยตรง จึงเรียกเมธอดโดยส่งอินสแตนซ์ให้ Invoke-CimMethod
gci["Get-CimInstance"] --> inst["อ็อบเจกต์ CimInstance"]
inst -.-> dt["วันที่แปลงเป็น DateTime แล้ว"]
inst -.-> nom["ไม่มีเมธอดโดยตรง"]
inst --> icm["ส่งให้ Invoke-CimMethod"]
icm --> call["เรียกเมธอด"]
ภาพ 4: CimInstance ไม่มีเมธอด จึงเรียกเมธอดโดยส่งให้ Invoke-CimMethod
# เรียกเมธอดของอินสแตนซ์: ดึงเจ้าของแต่ละโพรเซส
Get-CimInstance -ClassName Win32_Process -Filter "Name = 'notepad.exe'" |
Invoke-CimMethod -MethodName GetOwner
# เรียกเมธอดสแตติกของคลาส: เริ่มโพรเซส
Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = 'notepad.exe' }
# ตรวจนิยามคลาส (รายการคุณสมบัติและเมธอด)
Get-CimClass -ClassName Win32_Process
3.2. ตารางย้ายจาก cmdlet WMI รุ่นเก่า
ตั้งแต่ PowerShell 6 เป็นต้นไป (รวม PowerShell 7 ปัจจุบัน) cmdlet WMI v1 ต่อไปนี้ ถูกลบแล้ว ฟังก์ชันเดียวกันมีในโมดูล CimCmdlets (WMI v2)2
| เก่า (ถึง Windows PowerShell 5.1) | ปัจจุบัน (cmdlet CIM) | หมายเหตุ |
|---|---|---|
Get-WmiObject |
Get-CimInstance |
แนวคิด -Filter / -Query เหมือนกัน |
Get-WmiObject -List |
Get-CimClass |
สำรวจคลาสและตรวจนิยาม |
Invoke-WmiMethod |
Invoke-CimMethod |
อาร์กิวเมนต์ส่งเป็นแฮชเทเบิลผ่าน -Arguments @{ } |
Register-WmiEvent |
Register-CimIndicationEvent |
สมัครรับเหตุการณ์ (หัวข้อ 6.3) |
Set-WmiInstance |
Set-CimInstance |
เปลี่ยนคุณสมบัติที่เขียนได้ |
Remove-WmiObject |
Remove-CimInstance |
ลบอินสแตนซ์ |
cmdlet CIM ใช้ได้บน Windows PowerShell 5.1 ด้วย ดังนั้น ของที่เขียนใหม่ควรเขียนฝั่ง CIM แม้จะรันบน 5.1 — แบบนั้นไม่เหลือต้นทุนการย้าย ภาพรวมการอยู่ร่วมและการย้ายระหว่าง 5.1 กับ 7 อยู่ที่ «ความต่างระหว่าง Windows PowerShell 5.1 กับ PowerShell 7»
flowchart TB
accTitle: เหตุที่ควรเขียนสคริปต์ใหม่ด้วย CIM
accDescr: สคริปต์ที่เขียนด้วย cmdlet WMI รันบน 5.1 ได้แต่ถูกลบตั้งแต่ PowerShell 6 จึงต้องเขียนใหม่ตอนย้าย ส่วน cmdlet CIM ใช้บน 5.1 ได้ด้วย จึงเขียนของใหม่ฝั่ง CIM จะไม่เหลือต้นทุนการย้าย
new["สคริปต์ที่เขียนใหม่"] --> q1{"เขียนด้วยอันใด?"}
q1 -->|cmdlet WMI| old["รันได้บน 5.1"]
q1 -->|cmdlet CIM| cur["ใช้ได้บน 5.1 ด้วย"]
old --> del["ถูกลบใน PowerShell 7"]
del --> rew["ต้องเขียนใหม่ตอนย้าย"]
cur --> norew["ไม่เหลือต้นทุนการย้าย"]
ภาพ 5: เขียนของใหม่ด้วย cmdlet CIM แล้วพอไป PowerShell 7 จะไม่ต้องเขียนใหม่
4. การสอบถามระยะไกล — เซสชัน CIM (WSMan เป็นค่าเริ่มต้น) และตัวเลือก DCOM
cmdlet CIM ถ้าไม่ระบุปลายทางจะต่อ WMI ท้องถิ่นด้วย COM หากระบุ -ComputerName จะสร้าง เซสชันชั่วคราวด้วยโปรโตคอล WSMan (WinRM) เพื่อเชื่อม เมื่อจะทำหลายปฏิบัติการกับคอมพิวเตอร์เครื่องเดิม การสร้างเซสชัน CIM แล้วนำกลับมาใช้ซ้ำดีกว่าด้านประสิทธิภาพ3
flowchart TB
accTitle: การเลือกวิธีเชื่อม CIM
accDescr: ไม่ระบุคือเชื่อม WMI ท้องถิ่นด้วย COM การระบุ ComputerName สร้างเซสชันชั่วคราว WSMan ทุกครั้งที่สอบถาม หลายปฏิบัติการกับปลายทางเดิมใช้ New-CimSession ซ้ำได้เปรียบด้านประสิทธิภาพ และปลายทางที่ยังไม่ตั้ง WinRM มีตัวเลือกโปรโตคอล DCOM
exec["รัน cmdlet CIM"] --> q1{"ระบุ ComputerName?"}
q1 -->|ไม่มี| local["ต่อ WMI ท้องถิ่นด้วย COM"]
q1 -->|มี| q2{"หลายปฏิบัติการกับเครื่องเดิม?"}
q2 -->|ครั้งเดียว| temp["เซสชันชั่วคราว WSMan"]
q2 -->|หลายครั้ง| sess["ใช้ New-CimSession ซ้ำ"]
temp -.-> cost["ถูกสร้างทุกครั้งที่สอบถาม"]
nowinrm["ปลายทางที่ยังไม่ตั้ง WinRM"] -.-> dcom["ตัวเลือกโปรโตคอล DCOM"]
ภาพ 6: การสอบถามระยะไกลใช้ WSMan เป็นค่าเริ่มต้น และการปฏิบัติการหลายครั้งกับปลายทางเดิมใช้เซสชัน CIM ซ้ำเป็นวิธีประจำ
# ครั้งเดียวใช้ -ComputerName (สร้างเซสชันชั่วคราวทุกครั้ง)
Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName Server01, Server02
# สอบถามซ้ำแล้วใช้เซสชัน 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
สำหรับปลายทางที่ต่อด้วย WSMan ไม่ได้ เช่น เครื่องเก่าที่ตั้ง WinRM ไม่ได้ เลือก โปรโตคอล DCOM ได้4
$dcom = New-CimSessionOption -Protocol Dcom
$session = New-CimSession -ComputerName OldServer -SessionOption $dcom
ข้อสมมติของการสอบถามระยะไกลมีดังนี้
- WinRM ต้องถูกตั้งบนปลายทาง
winrm quickconfigตั้งบริการให้เริ่มอัตโนมัติ สร้างตัวฟัง HTTP (พอร์ตเริ่มต้น 5985) และลงทะเบียนข้อยกเว้นไฟร์วอลล์ ในครั้งเดียว10 หากจะเชื่อมด้วย HTTPS (พอร์ตเริ่มต้น 5986) แค่นี้ไม่พอ — ต้องเตรียมใบรับรองเซิร์ฟเวอร์ แล้วตั้งตัวฟัง HTTPS แยกด้วยอย่างwinrm quickconfig -transport:https10 - พอร์ตที่เกี่ยวข้องต้องเปิดบนไฟร์วอลล์ตามเส้นทาง งานปฏิบัติของการออกแบบและลงทะเบียนกฎขาเข้าอยู่ในบทความ «ไฟร์วอลล์ Windows กับแอปธุรกิจ»
- การยืนยันตัวตน ในโดเมน Kerberos ให้การยืนยันตัวตนร่วม ในเวิร์กกรุ๊ปไม่มี Kerberos จึงอาจต้องลงทะเบียนปลายทางใน
TrustedHostsของฝั่งลูกข่าย จำกัดรายการนั้นแคบที่สุดเท่าที่ได้10 - สิทธิ์ ตามการตั้งค่าเริ่มต้น การสอบถามและปฏิบัติการ WMI ระยะไกลทำด้วยบัญชีที่อยู่ในกลุ่มผู้ดูแลของปลายทางเป็นหลัก หากจะเปิดให้ผู้ใช้ทั่วไป ต้องตั้งสิทธิ์เข้าถึงทั้งบน WinRM และเนมสเปซ WMI10
- ทั้งนี้ DCOM ไม่มีพอร์ตรับฟังคงที่ (ใช้พอร์ตไดนามิกของ RPC) จึงออกแบบข้ามไฟร์วอลล์ได้ยาก สำหรับกลไกที่สร้างจากนี้ไป ปลอดภัยที่สุดคือถือ WSMan เป็นค่าเริ่มต้น
flowchart TB
accTitle: การตรวจข้อสมมติของการสอบถามระยะไกล
accDescr: บนปลายทาง winrm quickconfig ตั้งบริการให้เริ่มอัตโนมัติ สร้างตัวฟัง HTTP และลงทะเบียนข้อยกเว้นไฟร์วอลล์ในครั้งเดียว ตัวฟัง HTTPS ต้องเตรียมใบรับรองแล้วตั้งแยก และในเวิร์กกรุ๊ปอาจต้องลงทะเบียน TrustedHosts
qc["winrm quickconfig"] --> svc["ตั้งบริการให้เริ่มอัตโนมัติ"]
qc --> lis["สร้างตัวฟัง HTTP(5985)"]
qc --> fw["ข้อยกเว้นไฟร์วอลล์"]
lis ~~~ https["ตัวฟัง HTTPS(5986)"]
https -.-> cert["เตรียมใบรับรองแล้วตั้งแยก"]
fw ~~~ wg["สภาพแวดล้อมเวิร์กกรุ๊ป"]
wg -.-> th["ลงทะเบียน TrustedHosts"]
ภาพ 7: winrm quickconfig ทำการตั้งค่าเริ่มต้นในครั้งเดียว ตัวฟัง HTTPS และการยืนยันตัวตนในเวิร์กกรุ๊ปต้องจัดการแยก
5. การใช้จาก C# — System.Management กับ Microsoft.Management.Infrastructure
API สำหรับใช้ WMI จาก C# มีสองสาย ทั้งคู่เป็น Windows เท่านั้น
| System.Management | Microsoft.Management.Infrastructure (MI API) | |
|---|---|---|
| การนำเข้า | รวมใน .NET Framework ตามมาตรฐาน บน .NET ปัจจุบันคือแพ็กเกจ NuGet System.Management5 | แพ็กเกจ NuGet Microsoft.Management.Infrastructure6 |
| คลาสทางเข้า | ManagementObjectSearcher (ส่ง WQL เพื่อสอบถาม)5 |
CimSession (Create → QueryInstances / InvokeMethod / Subscribe)6 |
| ระบบชนิด | ManagementObject / ManagementEventWatcher11 |
CimInstance / CimSession — ชนิดเดียวกับ cmdlet CIM3 |
| ระยะไกล | ฐาน DCOM | ฐาน WSMan (เซสชัน CIM) มีรุ่นอะซิงโครนัส (*Async)6 |
| เหมาะกับ | การดึงข้อมูลท้องถิ่น การรักษาสินทรัพย์โค้ดเดิม | การใส่การสอบถามระยะไกลและการเฝ้า ดีไซน์ที่ใช้คู่กับ PowerShell |
5.1. System.Management: พื้นฐานของ ManagementObjectSearcher
ส่ง WQL เป็นสตริง แล้วรับคอลเลกชันผลผ่าน Get()5
// NuGet: System.Management (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"]} ว่าง {freeGb:F1} GB / ทั้งหมด {sizeGb:F1} GB");
}
คุณสมบัติคืนเป็น object ผ่านอินเด็กเซอร์ จึงต้องตรวจชนิด CIM จากเอกสารของคลาส (ในตัวอย่างนี้ FreeSpace / Size เป็น uint649) แล้วแคสต์ การคิดว่าเป็น int แล้วแคสต์จนได้ InvalidCastException คือกับดักแรกคลาสสิกตรงนี้
flowchart TB
accTitle: กับดักของการอ่านคุณสมบัติแล้วแคสต์
accDescr: คุณสมบัติของ System.Management คืนเป็น object ผ่านอินเด็กเซอร์ จึงต้องตรวจชนิด CIM จากเอกสารแล้วแคสต์ หากคิดว่าเป็น int แล้วแคสต์จะได้ InvalidCastException
idx["อ่านด้วยอินเด็กเซอร์"] --> obj["คืนเป็น object"]
obj --> chk["ตรวจชนิด CIM ในเอกสาร"]
chk --> cast["แคสต์เป็นชนิดที่ถูก"]
obj -.-> wrong["คิดว่าเป็น int แล้วแคสต์"]
wrong -.-> ex["InvalidCastException"]
ภาพ 8: คุณสมบัติคืนเป็น object จึงตรวจชนิด CIM ก่อนแล้วจึงแคสต์
5.2. MI API: พื้นฐานของ CimSession
CimSession จัดการท้องถิ่นและระยะไกลในรูปเดียวกัน มีการแจงนับ การสอบถาม การเรียกเมธอด การสมัครรับเหตุการณ์ และรุ่นอะซิงโครนัสครบในที่เดียว6
// NuGet: Microsoft.Management.Infrastructure (Windows เท่านั้น)
using Microsoft.Management.Infrastructure;
// ท้องถิ่นใช้ CimSession.Create(null) ระยะไกลส่งชื่อคอมพิวเตอร์
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} ว่าง {free / 1024.0 / 1024 / 1024:F1} GB");
}
เพราะทำงานกับ CimInstance ชนิดเดียวกับที่ cmdlet CIM ของ PowerShell คืน ไหลการพัฒนาแบบ «ทดลองใน PowerShell ก่อน แล้วคัดลอกไป C#» จึงต่อกันตามธรรมชาติ หากกำลังออกแบบการร่วมมือระหว่าง C# กับ PowerShell เอง ดู «วิธีรัน PowerShell จาก C# แล้วรับผลเป็นอ็อบเจกต์» ด้วย
flowchart TB
accTitle: ไหลจากการทดลองใน PowerShell ไปคัดลอกเป็น C#
accDescr: cmdlet CIM ของ PowerShell กับ MI API ของ C# ใช้ชนิด CimInstance เดียวกัน จึงทำให้ไหลการพัฒนาที่ทดลองใน PowerShell แล้วคัดลอกไป C# ต่อกันตามธรรมชาติ
trial["ทดลองใน PowerShell"] --> gci["cmdlet CIM"]
impl["อิมพลีเมนต์จริงใน C#"] --> mi["MI API"]
gci --> ci["ชนิด CimInstance เดียวกัน"]
mi --> ci
ci -.-> flow["คัดลอกต่อได้ตามธรรมชาติ"]
ภาพ 9: cmdlet CIM กับ MI API ใช้ชนิด CimInstance เดียวกัน จึงต่อจากการทดลองไปอิมพลีเมนต์จริงได้
6. สูตรที่ใช้บ่อย
6.1. ตารางอ้างอิงเร็วของคลาสประจำ
| ข้อมูลที่ต้องการ | คลาส | คุณสมบัติหลัก |
|---|---|---|
| ผู้ผลิต / ชื่อรุ่น | Win32_ComputerSystem |
Manufacturer, Model |
| หมายเลขซีเรียลตัวเครื่อง | Win32_BIOS |
SerialNumber |
| รุ่น OS / เวลาบูต | Win32_OperatingSystem |
Caption, Version, LastBootUpTime |
| พื้นที่ว่างดิสก์ | Win32_LogicalDisk |
DeviceID, FreeSpace, Size, DriveType9 |
| สถานะบริการ | Win32_Service |
Name, State, StartMode |
| รายการโพรเซส | Win32_Process |
Name, ProcessId, CommandLine |
6.2. ของประจำในการจัดการสินทรัพย์: หมายเลขซีเรียล ชื่อรุ่น และพื้นที่ว่างดิสก์
# ข้อมูลรุ่นและหมายเลขซีเรียล (สำหรับจับกับสมุดสินทรัพย์พีซี)
$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
}
# พื้นที่ว่างบนดิสก์ท้องถิ่น (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 คือค่าที่หมายถึง «ดิสก์ท้องถิ่น» โดยตัดสื่อถอดได้ (2) ไดรฟ์เครือข่าย (4) และซีดี (5) ออก9 สำหรับการเฝ้า แค่ส่งสคริปต์นี้ไปแต่ละเซิร์ฟเวอร์ผ่านเซสชัน CIM ก็ได้ฐานของการเฝ้าดิสก์โดยไม่มีเอเจนต์
flowchart TB
accTitle: ฐานการเฝ้าดิสก์โดยไม่มีเอเจนต์
accDescr: การกรอง DriveType 3 ตัดสื่อถอดได้ ไดรฟ์เครือข่าย และซีดี เหลือเฉพาะดิสก์ท้องถิ่น แล้วนำสคริปต์เดียวกันไปแต่ละเซิร์ฟเวอร์ผ่านเซสชัน CIM จึงได้ฐานการเฝ้าดิสก์โดยไม่มีเอเจนต์
scr["สคริปต์ดึงพื้นที่ว่าง"] --> flt["กรอง DriveType = 3"]
flt -.-> exc["ตัดสื่อถอดได้ ฯลฯ"]
scr --> ses["ผ่านเซสชัน CIM"]
ses --> srvs["ส่งไปแต่ละเซิร์ฟเวอร์"]
srvs --> mon["เฝ้าโดยไม่มีเอเจนต์"]
ภาพ 10: สคริปต์ที่กรองเหลือดิสก์ท้องถิ่นแล้วนำไปแต่ละเซิร์ฟเวอร์ด้วยเซสชัน CIM คือฐานของการเฝ้า
6.3. การตรวจจับการเริ่มโพรเซส — การสมัครรับเหตุการณ์
อย่า «ดึง Win32_Process เป็นระยะด้วยพอลลิงแล้วดูส่วนต่าง» ให้ใช้ การสมัครรับเหตุการณ์ วิธีง่ายสุดในการจับการเริ่มโพรเซสคือสมัครรับ Win32_ProcessStartTrace (คลาสเหตุการณ์จากโปรไวด์เดอร์เคอร์เนลเทรซ มีคุณสมบัติอย่าง ProcessName / ProcessID / ParentProcessID8)
# รันจากเซสชัน PowerShell ที่ยกสิทธิ์ผู้ดูแล
$action = {
$name = $Event.SourceEventArgs.NewEvent.ProcessName
$id = $Event.SourceEventArgs.NewEvent.ProcessID
Write-Host "โพรเซสเริ่ม: $name (PID=$id)"
}
Register-CimIndicationEvent -ClassName Win32_ProcessStartTrace `
-SourceIdentifier ProcessStarted -Action $action
การสมัครรับมีผลตลอดเวลาที่เซสชัน PowerShell ที่ลงทะเบียนยังมีชีวิต และ -Action รันทุกครั้งที่โพรเซสเริ่ม ระวังอย่ารันคำสั่งยกเลิกต่อทันทีในชุดเดียวกัน — นั่นแค่ทำให้การสมัครรับหายก่อนการเฝ้าจะเริ่ม รันขั้นยกเลิก เมื่อจบการเฝ้าแล้วเท่านั้น
# เมื่อจบการเฝ้า: ยกเลิกการสมัครรับ
Unregister-Event -SourceIdentifier ProcessStarted
Register-CimIndicationEvent ลงทะเบียนการสมัครรับด้วยชื่อคลาสหรือคิวรีเหตุการณ์ WQL และบล็อกสคริปต์ -Action รันทุกครั้งที่เหตุการณ์เข้ามา7 การสมัครรับคลาสนี้ ต้องมีสิทธิ์ผู้ดูแล7 ว่าใครรับเหตุการณ์ได้ถูกควบคุมด้วยตัวอธิบายความปลอดภัยของคลาสเหตุการณ์ และผู้ใช้ทั่วไปที่ไม่ได้ยกสิทธิ์จะถูกปฏิเสธการเข้าถึง8
sequenceDiagram
accTitle: ไหลการสมัครรับเหตุการณ์การเริ่มโพรเซส
accDescr: เซสชัน PowerShell ที่ยกสิทธิ์ผู้ดูแลลงทะเบียนการสมัครรับด้วย Register-CimIndicationEvent เหตุการณ์เข้าทุกครั้งที่โพรเซสเริ่มแล้วรัน Action และเมื่อจบการเฝ้าจึงยกเลิกด้วย Unregister-Event
participant ps as เซสชัน PowerShell
participant wmi as WMI
ps->>wmi: ลงทะเบียนสมัครรับด้วย Register-CimIndicationEvent
Note over ps: รันด้วยสิทธิ์ผู้ดูแล
wmi-->>ps: เหตุการณ์เข้าทุกครั้งที่โพรเซสเริ่ม
ps->>ps: รัน -Action
ps->>wmi: ยกเลิกด้วย Unregister-Event(เมื่อจบการเฝ้า)
ภาพ 11: การสมัครรับมีผลตลอดที่เซสชันที่ลงทะเบียนยังมีชีวิต และการยกเลิกทำเมื่อจบการเฝ้า
อีกวิธีคือ เหตุการณ์สร้างอินสแตนซ์ ทั่วไป (__InstanceCreationEvent) ที่ใช้กับคลาสใดก็ได้ ฝั่งนี้ WMI พอลลิงตามช่วงที่ระบุด้วย WITHIN แล้วทำให้ส่วนต่างเป็นเหตุการณ์ ดังนั้นคุณต้องตัดสินการแลกระหว่างช่วงตรวจจับกับภาระเอง
flowchart TB
accTitle: สองวิธีสมัครรับเพื่อตรวจจับการเริ่มโพรเซส
accDescr: Win32_ProcessStartTrace คือวิธีสมัครรับคลาสเหตุการณ์ของโปรไวด์เดอร์เคอร์เนลเทรซ ส่วน __InstanceCreationEvent ทั่วไปให้ WMI พอลลิงตามช่วง WITHIN แล้วทำให้ส่วนต่างเป็นเหตุการณ์ จึงต้องตัดสินการแลกช่วงเวลากับภาระเอง
goal["ตรวจจับการเริ่มโพรเซส"] --> t1["Win32_ProcessStartTrace"]
goal --> t2["__InstanceCreationEvent"]
t1 -.-> k1["สมัครรับเคอร์เนลเทรซ"]
t2 -.-> w1["พอลลิงตามช่วง WITHIN"]
w1 -.-> tr["แลกช่วงเวลากับภาระ"]
ภาพ 12: จะสมัครรับคลาสเหตุการณ์เฉพาะ หรือใช้เหตุการณ์สร้างอินสแตนซ์ทั่วไปพร้อมช่วงพอลลิง
# เฝ้าอินสแตนซ์ใหม่ของ Win32_Process ด้วยพอลลิงทุก 5 วินาที
$query = "SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process'"
Register-CimIndicationEvent -Query $query -SourceIdentifier ProcPoll -Action {
Write-Host "เริ่ม: $($Event.SourceEventArgs.NewEvent.TargetInstance.Name)"
}
ใน C# (System.Management) ManagementEventWatcher ทำหน้าที่เดียวกัน11
using System.Management;
// ในโพรเซสที่รันด้วยสิทธิ์ผู้ดูแล
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($"โพรเซสเริ่ม: {name} (PID={pid})");
};
watcher.Start();
// เมื่อจบการเฝ้า อย่าลืม watcher.Stop() และ Dispose
หากจะใส่เข้าส่วนเฝ้าที่รันต่อเนื่อง ให้รวมการลงทะเบียนใหม่เมื่อการสมัครรับขาด (ตอนบริการรีสตาร์ตหรือตอนผิดพลาด) ไว้ในการออกแบบด้วย ข้อคิดด้านการออกแบบของ «การตรวจและแสดงสถานะ» รวมการเฝ้าอุปกรณ์ อยู่ใน «แนวปฏิบัติการตรวจและแสดงสถานะอุปกรณ์ภายนอก»
stateDiagram-v2
accTitle: วงจรชีวิตการสมัครรับของการเฝ้าแบบรันต่อเนื่อง
accDescr: ในการเฝ้าแบบรันต่อเนื่อง สถานะกำลังสมัครรับอาจขาดเมื่อบริการรีสตาร์ตหรือมีข้อผิดพลาด จึงต้องรวมการออกแบบที่ตรวจว่าขาดแล้วลงทะเบียนใหม่จนกลับไปกำลังสมัครรับ
s1: กำลังสมัครรับ
s2: การสมัครรับขาดแล้ว
s3: ลงทะเบียนใหม่
[*] --> s1
s1 --> s2: บริการรีสตาร์ต・ข้อผิดพลาด
s2 --> s3
s3 --> s1
ภาพ 13: การเฝ้าแบบรันต่อเนื่องต้องรวมการออกแบบที่ลงทะเบียนใหม่เมื่อการสมัครรับขาด แล้วกลับไปกำลังสมัครรับ
7. กับดัก — ประสิทธิภาพ สิทธิ์ 64bit รีโพสิทอรี และวันที่
7.1. SELECT * กับพอลลิงที่ถี่เกินไป
คิวรี WMI คือ «โปรไวด์เดอร์สร้างค่า ณ เวลานั้น» — ไม่ได้ฟรี มีแอนติแพตเทิร์นคลาสสิกสองอย่าง
- การใช้
SELECT *ตามเคย การดึงทุกคุณสมบัติทุกแถวของWin32_Processทำให้ภาระของโปรไวด์เดอร์และการส่งเครือข่าย (เมื่อเป็นระยะไกล) พองตามนั้น กรองแถวด้วย-Filterกรองคอลัมน์ด้วย-Propertyและใช้-KeyOnlyถ้าต้องการแค่คีย์สำหรับขั้นถัดไป ทั้งหมดนี้เป็นเครื่องมือที่ทางการจัดไว้ «เพื่อลดขนาดอ็อบเจกต์และทราฟฟิกเครือข่าย»3 - พอลลิงช่วงสั้น ดีไซน์อย่าง «
Get-CimInstance Win32_Processทุกวินาที» ควรแทนด้วยการสมัครรับเหตุการณ์จากหัวข้อ 6.3 แม้ต้องใช้แบบพอลลิง (WITHIN) ก็ขยายช่วงให้พอเพียงต่อข้อกำหนดจริง
นอกจากนี้ การวน -ComputerName ทีละเครื่องกับปลายทางระยะไกลก็สิ้นเปลือง เพราะเซสชันชั่วคราวถูกสร้างทุกคิวรี — เปลี่ยนเป็น ใช้เซสชัน CIM ซ้ำ สำหรับหลายปฏิบัติการ3
flowchart TB
accTitle: แอนติแพตเทิร์นด้านประสิทธิภาพและจุดที่แทน
accDescr: การใช้ SELECT ดอกจันตามเคยให้กรองแถวและคอลัมน์ด้วย Filter กับ Property ถ้าต้องการแค่คีย์ใช้ KeyOnly พอลลิงช่วงสั้นให้แทนด้วยการสมัครรับเหตุการณ์ และการวน ComputerName ทีละเครื่องให้ใช้เซสชัน CIM ซ้ำ
a1["ใช้ SELECT * ตามเคย"] --> f1["กรองด้วย Filter และ Property"]
f1 -.-> f2["ถ้าต้องการแค่คีย์ใช้ KeyOnly"]
a2["พอลลิงช่วงสั้น"] --> f3["แทนด้วยการสมัครรับเหตุการณ์"]
a3["ComputerName ทีละเครื่อง"] --> f4["ใช้เซสชัน CIM ซ้ำ"]
ภาพ 14: การกรองแถว คอลัมน์ และคีย์ กับการแทนพอลลิงด้วยการสมัครรับเหตุการณ์ ป้องกันปัญหาประสิทธิภาพของ WMI ได้ประมาณครึ่งหนึ่ง
7.2. สิทธิ์ของการสมัครรับเหตุการณ์
ตามหัวข้อ 6.3 การสมัครรับตระกูล Win32_ProcessStartTrace สมมติสิทธิ์ผู้ดูแล7 «บนเครื่องพัฒนา (รันเป็นผู้ดูแล) ใช้ได้ แต่การเฝ้าไม่ทำงานในสภาพแวดล้อมผู้ใช้ทั่วไปของลูกค้า» คืออุบัติเหตุคลาสสิก คู่กับไดอะล็อกแจ้งไฟร์วอลล์ หากจะใส่การเฝ้าในแอปธุรกิจที่รันเป็นผู้ใช้ทั่วไป พิจารณาแยกส่วนเฝ้าออกเป็นบริการ Windows (รันเป็น LocalSystem เป็นต้น) แล้วเชื่อมกับตัวแอปผ่านการสื่อสารข้ามโพรเซส
flowchart TB
accTitle: โครงเฝ้าในสภาพแวดล้อมผู้ใช้ทั่วไป
accDescr: การสมัครรับที่สมมติสิทธิ์ผู้ดูแลถูกตัดออกจากตัวแอปที่รันเป็นผู้ใช้ทั่วไป แยกส่วนเฝ้าเป็นบริการ Windows ที่รันเป็น LocalSystem ฯลฯ แล้วเชื่อมกับตัวแอปด้วยการสื่อสารข้ามโพรเซส
svcm["บริการ Windows สำหรับเฝ้า"] --> subm["สมัครรับเทรซการเริ่ม"]
svcm -.-> lsm["รันเป็น LocalSystem ฯลฯ"]
appm["ตัวแอป(ผู้ใช้ทั่วไป)"] ---|การสื่อสารข้ามโพรเซส| svcm
ภาพ 15: การสมัครรับที่ต้องมีสิทธิ์ผู้ดูแลแยกไปฝั่งบริการ และเชื่อมกับตัวแอปด้วยการสื่อสารข้ามโพรเซส
7.3. 32bit/64bit กับโปรไวด์เดอร์
บน Windows 64bit บางโปรไวด์เดอร์มีทั้งรุ่น 32bit และ 64bit และโดยค่าเริ่มต้น ฝั่งที่ตรงกับจำนวนบิตของแอปที่เรียก เป็นผู้ตอบ12 ตัวอย่างคลาสสิกคือโปรไวด์เดอร์รีจิสทรี (StdRegProv) ใต้ root\default: การอ่านจากแอป 32bit ได้ค่าฝั่ง Wow6432Node (วิว 32bit)12 ถ้า «ค่าที่อ่านผ่าน WMI ไม่ตรงกับที่เห็นใน regedit» ให้สงสัยอันนี้ก่อน หากต้องการวิวฝั่งตรงข้าม ขออย่างชัดได้โดยตั้ง __ProviderArchitecture (และถ้าจะบังคับ __RequiredArchitecture) ในคอนเท็กซ์ตอนเชื่อม12 ภาพรวมของปัญหาจำนวนบิตยังอยู่ใน «เรียก Win32 API จาก C# อย่างปลอดภัย — คู่มือปฏิบัติ P/Invoke»
flowchart TB
accTitle: การเลือกโปรไวด์เดอร์บนสภาพแวดล้อม 64bit
accDescr: โดยค่าเริ่มต้นโปรไวด์เดอร์ที่ตรงกับจำนวนบิตของแอปที่เรียกเป็นผู้ตอบ การสอบถามรีจิสทรีจากแอป 32bit ได้ค่าฝั่ง Wow6432Node แต่ระบุ __ProviderArchitecture แล้วขอวิวฝั่งตรงข้ามอย่างชัดได้
q1{"บิตของตัวเรียกคือ?"} -->|32bit| p32["โปรไวด์เดอร์ 32bit ตอบ"]
q1 -->|64bit| p64["โปรไวด์เดอร์ 64bit ตอบ"]
p32 -.-> wow["รีจิสทรีเป็นค่าฝั่ง Wow6432Node"]
ctx["ระบุ __ProviderArchitecture"] -.-> ov["ขอวิวฝั่งตรงข้ามอย่างชัด"]
ภาพ 16: โดยค่าเริ่มต้นฝั่งที่ตรงกับจำนวนบิตของตัวเรียกเป็นผู้ตอบ ดังนั้นแอป 32bit อ่านฝั่ง Wow6432Node
7.4. อาการและการรับมือเมื่อรีโพสิทอรี WMI เสีย
นิยามคลาสของ WMI ถูกเก็บในรีโพสิทอรี (ไม่ใช่ไฟล์เดียว — กลุ่มไฟล์ในโฟลเดอร์ Repository ทำงานเป็นฐานข้อมูล13) เมื่อมันไม่สอดคล้อง ข้อผิดพลาดอย่าง «หาคลาสที่ควรมีไม่เจอ» หรือ «เนมสเปซไม่ถูกต้อง» เริ่มโผล่ ทั้งที่ฝั่งแอปไม่ได้เปลี่ยนอะไร ใช้ winmgmt.exe เพื่อแยกสาเหตุและซ่อม13
rem ตรวจความสอดคล้อง (ผล inconsistent แปลว่าไม่สอดคล้อง)
winmgmt /verifyrepository
rem ตรวจความสอดคล้อง แล้วสร้างใหม่หากไม่สอดคล้อง (เนื้อหาที่อ่านได้ถูกรวม)
winmgmt /salvagerepository
สิ่งที่ต้องจำคือ อย่าทำให้การลบหรือรีเซ็ตรีโพสิทอรีเป็นก้าวแรก ข้อผิดพลาดที่โผล่ผ่าน WMI อาจมาจากส่วนอื่นของ OS และ Microsoft เองระบุชัดว่าการลบรีโพสิทอรีเป็นขั้นแรก «อาจทำให้ระบบหรือแอปที่ติดตั้งเสียหายได้»13 รักษาลำดับ: ตรวจด้วย /verifyrepository แล้วซ่อมด้วย /salvagerepository
flowchart TB
accTitle: ขั้นตอนแยกสาเหตุเมื่อรีโพสิทอรี WMI ไม่สอดคล้อง
accDescr: เมื่อมีข้อผิดพลาดอย่างหาคลาสไม่เจอ ให้ตรวจความสอดคล้องด้วย winmgmt verifyrepository หากไม่สอดคล้องให้สร้างใหม่ด้วย salvagerepository และอย่าลบหรือรีเซ็ตรีโพสิทอรีเป็นก้าวแรก
sym["ข้อผิดพลาดเช่นหาคลาสไม่เจอ"] --> verify["winmgmt /verifyrepository"]
verify --> q1{"ผลคือ inconsistent?"}
q1 -->|ใช่| salvage["winmgmt /salvagerepository"]
q1 -->|ไม่ใช่| other["สงสัยสาเหตุส่วนอื่นของ OS"]
salvage -.-> merge["เนื้อหาที่อ่านได้ถูกรวม"]
del["ลบหรือรีเซ็ตรีโพสิทอรี"] -.-> ng["อย่าใช้เป็นก้าวแรก"]
ภาพ 17: รักษาลำดับตรวจด้วย verify แล้วซ่อมด้วย salvage อย่าลบเป็นก้าวแรก
7.5. การแปลงรูปแบบวันที่ DMTF
วันที่ WMI ถูกเก็บเป็นสตริงในรูปแบบ DMTF ตามข้อกำหนด CIM: yyyymmddHHMMSS.mmmmmm±UUU (ค่าท้ายคือออฟเซ็ตจาก UTC เป็นนาที เช่น 20260801100000.000000+540) อย่าตัดแปะค่าดิบด้วยการประมวลผลสตริง ให้ใช้ API แปลงแทน
- C# (System.Management):
ManagementDateTimeConverterให้การแปลงระหว่างรูปแบบ DMTF กับDateTime/TimeSpan11 - API ตระกูล CIM (Get-CimInstance / MI API): คุณสมบัติวันที่กลับมาแปลงเป็น
DateTimeแล้ว จึงไม่เจอปัญหานี้ตั้งแต่ต้น(Get-CimInstance Win32_OperatingSystem).LastBootUpTimeใช้เป็นDateTimeในคำนวณได้เลย
flowchart TB
accTitle: การจัดการรูปแบบวันที่ DMTF
accDescr: วันที่ WMI ถูกเก็บเป็นสตริงรูปแบบ DMTF หากอ่านค่าดิบด้วย System.Management ให้แปลงด้วย ManagementDateTimeConverter ส่วน API ตระกูล CIM คืน DateTime ที่แปลงแล้ว จึงอย่าตัดแปะสตริงเอง
dmtf["สตริงรูปแบบ DMTF"] --> q1{"ดึงด้วย API ใด?"}
q1 -->|System.Management| conv["ManagementDateTimeConverter"]
q1 -->|API ตระกูล CIM| done["คืนเป็น DateTime ที่แปลงแล้ว"]
conv --> dtv["แปลงเป็น DateTime / TimeSpan"]
cut["ตัดแปะสตริงเอง"] -.-> ng["อย่าใช้"]
ภาพ 18: การแปลงสตริง DMTF ให้มอบหมายแก่ API แปลง และถ้าเป็น API ตระกูล CIM ให้ใช้ DateTime ที่แปลงแล้วตามที่เป็น
8. ตอนที่ไม่ควรใช้ WMI — ตารางตัดสินเครื่องมือ
WMI เก่งในฐานะ «ปากอ่านเดียวกัน» แต่ไม่ใช่คำตอบที่ดีที่สุดเสมอ นี่คือเกณฑ์ใช้งานจริงในการเลือกเครื่องมือ
| สิ่งที่อยากทำ | เครื่องมือที่เหมาะ | เหตุที่ไม่เลือก WMI |
|---|---|---|
| ดึงข้อมูลฮาร์ดแวร์และโครง OS สอบถามระยะไกลโดยไม่มีเอเจนต์ | WMI/CIM | นี่คือสนามหลักของ WMI — รวมกว่าการเคาะ API เฉพาะทีละตัว |
| อ่านเขียนการตั้งค่าของแอปเอง | อ่านรีจิสทรีตรง (Microsoft.Win32.Registry) หรือไฟล์โครง |
การจัดการรีจิสทรีผ่าน WMI เป็นทางอ้อม และยังรับปัญหาจำนวนบิตจากหัวข้อ 7.3 ไปด้วย |
| เฝ้าประสิทธิภาพต่อเนื่องความถี่สูงอย่างการใช้ CPU | เคาน์เตอร์ประสิทธิภาพ (System.Diagnostics.PerformanceCounter เป็นต้น) |
เคาน์เตอร์ถูกสร้างมาเพื่องานนี้ พอลลิง WMI ช่วงสั้นแพ้ทั้งภาระและความแม่น |
| เรียกฟังก์ชัน OS ครั้งเดียว หรืองานที่ต้องการความหน่วงต่ำ | Win32 API (P/Invoke) | WMI มีโสหุ้ยของการผ่าน COM/โปรไวด์เดอร์ |
| แจงและจัดการโพรเซสท้องถิ่นเมื่อสิทธิ์ของโพรเซสเองพอ | System.Diagnostics.Process |
จบในไลบรารีมาตรฐาน ลดการพึ่งพา |
| ตั้งคุณลักษณะจัดการ Windows อย่างไฟร์วอลล์หรือเครือข่าย | cmdlet CIM เฉพาะงาน เช่น Get-NetFirewallRule |
ชุด cmdlet ที่จัดตามงานเฉพาะแม่นและปลอดภัยกว่าการไล่ล่าคลาส WMI ดิบ |
| ตรวจจับการเปลี่ยนไฟล์หรือโฟลเดอร์ | FileSystemWatcher |
อย่านำ WMI เข้าพื้นที่ที่มี API เฉพาะอยู่แล้ว |
แกนการตัดสินเรียบ: «ในพื้นที่ที่มีกลไกเฉพาะ ให้ใช้กลไกเฉพาะ และสงวน WMI/CIM ไว้สำหรับการสอบถามข้ามขอบเขตและการสอบถามระยะไกล» ตัวอย่าง Get-NetFirewallRule ในแถวท้าย ภายในเป็นชุด cmdlet ที่สร้างบน CIM — กรณี «รับประโยชน์ของ WMI/CIM โดยไม่แตะตรง»
flowchart TB
accTitle: แกนการตัดสินเครื่องมือ
accDescr: ในพื้นที่ที่มีกลไกเฉพาะให้ใช้กลไกเฉพาะ และในพื้นที่ที่ไม่มี ให้ใช้ WMI กับ CIM สำหรับการสอบถามข้ามขอบเขตและการสอบถามระยะไกล โดย cmdlet CIM เฉพาะงานเป็นรูปที่รับประโยชน์โดยไม่แตะ WMI กับ CIM โดยตรง
q1{"มีกลไกเฉพาะอยู่แล้ว?"} -->|มี| ded["ใช้กลไกเฉพาะ"]
q1 -->|ไม่มี| wmi["ใช้ WMI / CIM"]
wmi -.-> use["สอบถามข้ามขอบเขต・ระยะไกล"]
cmd["cmdlet CIM เฉพาะงาน"] -.-> ben["รับประโยชน์โดยไม่แตะตรง"]
ภาพ 19: ในพื้นที่ที่มีกลไกเฉพาะให้ใช้ของเฉพาะ และใช้ WMI/CIM สำหรับการสอบถามข้ามขอบเขตและการสอบถามระยะไกล
9. สรุป
- CIM คือมาตรฐานอุตสาหกรรมของ DMTF และ WMI คืออิมพลีเมนต์ของ Microsoft ทั้ง cmdlet CIM ของ PowerShell และ MI API ของ C# คือทางเข้ารุ่นปัจจุบันที่ตามมาตรฐานนี้
- ใน PowerShell Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent คือรุ่นปัจจุบัน Get-WmiObject และ cmdlet WMI อื่นไม่มีใน PowerShell 7 จึงเขียนสคริปต์ใหม่ฝั่ง CIM แม้จะเล็ง 5.1
- การสอบถามระยะไกลใช้ WSMan (WinRM) เป็นค่าเริ่มต้น และหลายปฏิบัติการควรใช้เซสชัน CIM ซ้ำ สำหรับปลายทางที่ยังไม่ตั้ง WinRM DCOM คือทางหนีที่มีอยู่
- ใน C# เลือกระหว่าง System.Management (สะดวก เน้นท้องถิ่น) กับ Microsoft.Management.Infrastructure (เน้นระยะไกลและการเฝ้า ใช้ระบบชนิดเดียวกับ cmdlet CIM) ทั้งคู่เป็นแพ็กเกจ NuGet เฉพาะ Windows
- เฝ้าโพรเซสด้วยการสมัครรับเหตุการณ์ ไม่ใช่พอลลิง การสมัครรับ Win32_ProcessStartTrace ต้องมีสิทธิ์ผู้ดูแล
- เลี่ยง SELECT * และพอลลิงช่วงสั้น — กรองด้วย -Filter / -Property / -KeyOnly จำไว้ว่าคิวรีจากโพรเซส 32bit ถูกตอบโดยโปรไวด์เดอร์ 32bit วันที่ DMTF ใช้ API แปลง และรีโพสิทอรีที่เสียให้รับมือตามลำดับ verify → salvage ไม่ใช่การลบ
- อย่านำ WMI เข้าพื้นที่ที่มีกลไกเฉพาะอยู่แล้ว (การตั้งค่า เคาน์เตอร์ประสิทธิภาพ การเรียก API ครั้งเดียว) — สงวน WMI/CIM ไว้สำหรับการสอบถามข้ามขอบเขตและการสอบถามระยะไกล ประโยคเดียวนี้สรุปที่ยืนของมัน
บทความที่เกี่ยวข้อง
- วิธีรัน PowerShell จาก C# (CSharp) แล้วรับผลเป็นอ็อบเจกต์
- สูตรคำสั่ง PowerShell ปฏิบัติ — เพิ่มเครื่องมือเล็กที่ใช้ทุกวัน
- ความต่างระหว่าง Windows PowerShell 5.1 กับ PowerShell 7 — คู่มือปฏิบัติการย้ายสคริปต์ในองค์กร
- แนวปฏิบัติการตรวจและแสดงสถานะอุปกรณ์ภายนอก — ออกแบบให้เกินแค่ «เชื่อมต่อแล้ว»
- เรียก Win32 API จาก C# อย่างปลอดภัย — คู่มือปฏิบัติ P/Invoke (DllImport / LibraryImport / CsWin32)
- TPM ของ Windows คืออะไร — คู่มือภาพ «ตู้นิรภัยที่ไม่ปล่อยกุญแจออก» และการบูตแบบวัด
ด้านให้คำปรึกษาที่เกี่ยวข้อง
KomuraSoft LLC รับการฝังการดึงข้อมูลฮาร์ดแวร์ การเฝ้าโพรเซส และการสอบถามพีซีระยะไกลด้วย WMI/CIM เข้าแอปธุรกิจ การย้ายสคริปต์ในองค์กรที่อยู่บน Get-WmiObject ไป cmdlet CIM และการสอบสวนสาเหตุอย่าง «บนเครื่องพัฒนาใช้ได้ แต่ที่ลูกค้าติดข้อผิดพลาดสิทธิ์» ให้ปรึกษาเป็นเส้นเดียวตั้งแต่ทดลองใน PowerShell จนถึงอิมพลีเมนต์จริงใน C# ได้
ลิงก์อ้างอิง
-
Microsoft Learn, About WMI. ว่าด้วย WMI ที่เป็นอิมพลีเมนต์ของ Microsoft สำหรับ WBEM (โครงการอุตสาหกรรมที่พัฒนาเทคโนโลยีมาตรฐานสำหรับการเข้าถึงข้อมูลการจัดการในสภาพแวดล้อมองค์กร) ว่าด้วยการแสดงเป้าการจัดการด้วยมาตรฐานอุตสาหกรรม CIM (Common Information Model) ที่ DMTF (Distributed Management Task Force) พัฒนาและดูแล ว่าด้วย MI (Windows Management Infrastructure) รุ่นถัดไปที่เข้ากันได้เต็มกับ WMI เดิม และว่าด้วยการเชื่อม WMI ระยะไกลที่ทำด้วย DCOM โดยมี WinRM ฐาน WS-Management เป็นทางเลือก ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Differences between Windows PowerShell 5.1 and PowerShell 7.x. ว่าด้วย cmdlet WMI v1 (Register-WmiEvent / Set-WmiInstance / Invoke-WmiMethod / Get-WmiObject / Remove-WmiObject) ที่ถูกลบจาก PowerShell และว่าด้วย cmdlet ของโมดูล CimCmdlets (WMI v2) ที่ให้ฟังก์ชันเดียวกันพร้อมคุณลักษณะใหม่และไวยากรณ์ที่ออกแบบใหม่ ↩ ↩2
-
Microsoft Learn, Get-CimInstance (CimCmdlets). ว่าด้วยการเชื่อม WMI ท้องถิ่นด้วยเซสชัน COM เมื่อไม่ได้ระบุทั้ง ComputerName และ CimSession และการสร้างเซสชันชั่วคราวด้วยโปรโตคอล WsMan เมื่อระบุ -ComputerName ว่าด้วยการเชื่อมผ่านเซสชัน CIM ที่แนะนำด้านประสิทธิภาพเมื่อทำหลายปฏิบัติการกับคอมพิวเตอร์เครื่องเดิม ว่าด้วย -Filter ที่เป็นประโยค where ของ WQL/CQL โดยไม่รวมคำว่า WHERE ว่าด้วย -Property และ -KeyOnly ที่ลดขนาดอ็อบเจกต์และทราฟฟิกเครือข่าย ว่าด้วยเนมสเปซเริ่มต้นที่เป็น root/CIMV2 และภาษาสอบถามเริ่มต้น (-QueryDialect) ที่เป็น WQL ว่าด้วยเอาต์พุตที่เป็น Microsoft.Management.Infrastructure.CimInstance ว่าด้วยตัวอย่างการเรียก GetOwner คู่กับ Invoke-CimMethod และว่าด้วย cmdlet ที่เป็น Windows เท่านั้น ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10
-
Microsoft Learn, New-CimSessionOption (CimCmdlets). ว่าด้วยออปชันเซสชัน CIM ที่มีสองชุดพารามิเตอร์ สำหรับ WsMan และสำหรับ DCOM ว่าด้วย -Protocol ที่รับ Dcom / Default / Wsman ว่าด้วยตัวอย่างการส่งออปชันที่สร้างด้วย New-CimSessionOption -Protocol Dcom เข้า -SessionOption ของ New-CimSession เพื่อสร้างเซสชัน CIM แบบ DCOM และว่าด้วยระดับการปลอมตัวเริ่มต้นของเซสชัน DCOM ที่เป็น Impersonate ↩ ↩2
-
Microsoft Learn, ManagementObjectSearcher Class (System.Management). ว่าด้วยคลาสทางเข้าที่ใช้บ่อยที่สุดในการดึงข้อมูลการจัดการ โดยดึงคอลเลกชันของอ็อบเจกต์การจัดการตามคิวรี WQL ที่ระบุ ว่าด้วยการรับ ObjectQuery และ ManagementScope (เนมสเปซ WMI) แล้วคืน ManagementObjectCollection ผ่าน Get() และว่าด้วย System.Management.dll ที่ให้เป็นแพ็กเกจ NuGet System.Management ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, CimSession Class (Microsoft.Management.Infrastructure). ว่าด้วย Microsoft.Management.Infrastructure.dll ที่ให้เป็นแพ็กเกจ NuGet Microsoft.Management.Infrastructure ว่าด้วยการสร้างเซสชันผ่าน Create(computerName) ว่าด้วยการรันคิวรีผ่าน QueryInstances(namespace, queryDialect, query) และว่าด้วยการมี EnumerateInstances / GetInstance / InvokeMethod / Subscribe พร้อมรุ่นอะซิงโครนัส (*Async) ของแต่ละตัว ขณะอิมพลีเมนต์ IDisposable ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Register-CimIndicationEvent (CimCmdlets). ว่าด้วยการสมัครรับอินดิเคชัน (เหตุการณ์) ด้วยชื่อคลาสหรือนิพจน์คิวรี และการตั้งชื่อการสมัครรับด้วย -SourceIdentifier ว่าด้วยตัวอย่างการสมัครรับ Win32_ProcessStartTrace พร้อมหมายเหตุว่าต้องรัน PowerShell ในฐานะผู้ดูแล ว่าด้วยตัวอย่างการอ้าง ProcessName / ProcessId จาก $Event.SourceEventArgs.NewEvent ในบล็อกสคริปต์ -Action ว่าด้วยการเชื่อมผ่านเซสชันชั่วคราว WsMan เมื่อระบุ -ComputerName และการเชื่อมท้องถิ่นด้วย COM เมื่อไม่ได้ระบุ และว่าด้วยการใช้ Unregister-Event เพื่อยกเลิกการสมัครรับ ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Win32_ProcessStartTrace class. ว่าด้วยคลาสเหตุการณ์ที่บ่งชี้การเริ่มโพรเซสใหม่ มีคุณสมบัติอย่าง ProcessName / ProcessID / ParentProcessID / SessionID / Sid ว่าด้วยคุณสมบัติ SECURITY_DESCRIPTOR ที่เป็นตัวอธิบายซึ่งโปรไวด์เดอร์เหตุการณ์ใช้ตัดสินว่าผู้ใช้ใดรับเหตุการณ์ได้ และว่าด้วยเนมสเปซที่เป็น Root\CIMV2 ให้โดยโปรไวด์เดอร์เคอร์เนลเทรซ (Krnlprov.dll) ↩ ↩2 ↩3
-
Microsoft Learn, Win32_LogicalDisk class. ว่าด้วย Win32_LogicalDisk ที่เป็นคลาสสืบทอดจาก CIM_LogicalDisk แทนอุปกรณ์เก็บข้อมูลท้องถิ่น ว่าด้วยค่าของ DriveType (2 = สื่อถอดได้, 3 = ดิสก์ท้องถิ่น, 4 = ไดรฟ์เครือข่าย, 5 = ซีดี เป็นต้น) ว่าด้วย FreeSpace / Size ที่เป็นค่าไบต์ชนิด uint64 ว่าด้วย DeviceID ที่เป็นคีย์ และว่าด้วยตัวอย่างคิวรี VBScript / C# ที่กรองด้วย DriveType = 3 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Installation and configuration for Windows Remote Management. ว่าด้วยตัวฟัง WinRM ที่โดยค่าเริ่มต้นยังไม่ถูกตั้ง จึงส่งหรือรับข้อความ WS-Management ไม่ได้ ว่าด้วย winrm quickconfig ที่ตั้งบริการให้เริ่มอัตโนมัติ ตั้งตัวฟัง HTTP/HTTPS และลงทะเบียนข้อยกเว้นไฟร์วอลล์ ว่าด้วยพอร์ตเริ่มต้นของ WinRM 2.0 ที่เป็น HTTP 5985 / HTTPS 5986 ว่าด้วยการตั้ง TrustedHosts ให้แคบที่สุดเมื่อยืนยันตัวตนร่วม (Kerberos) ตั้งไม่ได้ เช่นในเวิร์กกรุ๊ป และว่าด้วยตัวอธิบายความปลอดภัยเริ่มต้น (RootSDDL) ที่ควบคุมการเข้าถึงตัวฟังจากระยะไกล รวมการตั้งเพิ่มที่ต้องมีหากจะอนุญาตผู้ใช้ที่ไม่ใช่ผู้ดูแลให้ใช้ปลั๊กอิน WMI ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, System.Management Namespace. ว่าด้วยเนมสเปซที่สอบถามโครงสร้าง WMI ผ่านตระกูลคลาส ManagementObjectSearcher และจัดการการสมัครรับเหตุการณ์ผ่าน ManagementEventWatcher ว่าด้วย WqlEventQuery ที่แทนคิวรีเหตุการณ์รูปแบบ WQL และว่าด้วย ManagementDateTimeConverter ที่ให้เมธอดแปลงระหว่างวันที่/เวลาและช่วงเวลาแบบ DMTF กับ DateTime / TimeSpan ของ CLR ↩ ↩2 ↩3
-
Microsoft Learn, Requesting WMI Data on a 64-bit Platform. ว่าด้วยกรณีที่โปรไวด์เดอร์มีทั้งรุ่น 32bit และ 64bit โดยค่าเริ่มต้นโปรไวด์เดอร์ 32bit ตอบแอป 32bit (รวมสคริปต์) และโปรไวด์เดอร์ 64bit ตอบแอป 64bit ว่าด้วยการขอหรือบังคับรุ่นโปรไวด์เดอร์ที่ไม่ใช่ค่าเริ่มต้นผ่าน __ProviderArchitecture (32 หรือ 64) และ __RequiredArchitecture ของคอนเท็กซ์ (ได้ WBEM_E_PROVIDER_LOAD_FAILURE หากบังคับรุ่นที่ไม่มี) และว่าด้วยตัวอย่างโปรไวด์เดอร์รีจิสทรีที่ลูกข่าย 32bit ได้รับข้อมูลฝั่ง HKLM\SOFTWARE\Wow6432Node ↩ ↩2 ↩3
-
Microsoft Learn, winmgmt. ว่าด้วย /verifyrepository ของ winmgmt.exe ที่ตรวจความสอดคล้องของรีโพสิทอรี WMI ว่าด้วย /salvagerepository ที่ตรวจความสอดคล้องแล้วหากพบความไม่สอดคล้องจะสร้างรีโพสิทอรีใหม่โดยรวมเนื้อหาที่อ่านได้ ว่าด้วย /resetrepository ที่คืนรีโพสิทอรีสู่สถานะตอนติดตั้ง OS ครั้งแรก ว่าด้วยรีโพสิทอรีที่ทำงานเป็นฐานข้อมูลจากกลุ่มไฟล์ในโฟลเดอร์ Repository และว่าด้วยข้อผิดพลาดที่โผล่ผ่าน WMI ที่บางครั้งมาจากส่วนอื่นของ OS จึงไม่ควรลบรีโพสิทอรีเป็นขั้นแรกเพราะอาจทำให้ระบบหรือแอปที่ติดตั้งเสียหายได้ ↩ ↩2 ↩3
บทความที่เกี่ยวข้อง
บทความล่าสุดที่มีแท็กเดียวกัน เพื่อเจาะลึกหัวข้อใกล้เคียง
แนวปฏิบัติมัลติเธรดเชิงปฏิบัติ: ฉบับ .NET — สิ่งที่ต้องตัดสินก่อนเพิ่มเธรด
เรียบเรียงหลักออกแบบที่กันไม่ให้โค้ดมัลติเธรด .NET/C# «แครชหรือค้างเป็นครั้งคราว»: อย่าสร้างเธรดเอง ให้ขี่ Task ลดสถานะที่เปลี่ยนแปลงร่วม...
Named Pipe ในทางปฏิบัติ — IPC มาตรฐานของ Windows ตั้งแต่การออกแบบถึงความปลอดภัย
คู่มือเชิงปฏิบัติเรื่อง named pipe ซึ่งเป็นการสื่อสารระหว่างโปรเซสมาตรฐานของ Windows บทความนี้จัดระเบียบจากแหล่งปฐมภูมิ ทั้งการเลือกระหว่...
แอปที่พังเมื่อรีซูมจากสลีป — อีเวนต์พลังงานของ Windows ทำงานอย่างไร และสร้างแอปธุรกิจที่รอดได้อย่างไร
เปิดแล็ปท็อปแล้วการเชื่อมต่อของแอปธุรกิจตาย — สาเหตุคือการออกแบบที่ไม่เคยคิดถึงสลีป บทความนี้ครอบคลุมลำดับการแจ้ง WM_POWERBROADCAST พฤติก...
Win32 Thread Pool API — คอนเคอร์เรนซีโดยไม่สร้างเธรดเอง ด้วย CreateThreadpoolWork
โค้ดเนทีฟของคุณกระจายการเรียก CreateThread ไปทั่วหรือไม่ บทความนี้อธิบาย Win32 thread pool API ที่ออกแบบใหม่ใน Vista — ออบเจ็กต์สี่ชนิด w...
DllMain และ Loader Lock — เหตุผลจริงที่ถูกบอกให้ "อย่าทำอะไรในการเริ่มต้น DLL"
ทำไมคุณต้องไม่เรียก LoadLibrary หรือซิงโครไนซ์กับเธรดอื่นจาก DllMain บทความนี้อธิบายจากแหล่งปฐมภูมิว่า loader lock ทำให้การแจ้ง DLL ทุกคร...
หัวข้อที่เกี่ยวข้อง
หน้าเหล่านี้วางหัวข้อไว้ในบริบทที่กว้างขึ้นของบริการและการตัดสินใจ
หัวข้อเทคนิคของ Windows
ประตูสู่การพัฒนา Windows การตรวจสอบบั๊ก และการใช้ประโยชน์จากสินทรัพย์เดิม
บริการที่เกี่ยวข้องกับหัวข้อนี้
บทความนี้เกี่ยวข้องโดยตรงกับบริการต่อไปนี้
พัฒนาแอป Windows
แอปธุรกิจ การเชื่อมต่ออุปกรณ์ และเครื่องมือสื่อสาร ตั้งแต่ความต้องการจนถึงการพัฒนา
คำถามที่พบบ่อย
คำถามที่มักพบในการปรึกษาเกี่ยวกับหัวข้อของบทความนี้
- WMI กับ CIM ต่างกันอย่างไร?
- CIM คือ «โมเดลมาตรฐานอุตสาหกรรมสำหรับแสดงเป้าการจัดการอย่างระบบและอุปกรณ์» ที่ DMTF (Distributed Management Task Force) กำหนดและดูแล WMI คืออิมพลีเมนต์ของ Microsoft สำหรับโครงการ WBEM ที่ใช้มาตรฐานนั้น และฝังอยู่ใน Windows กล่าวคือ CIM คือข้อกำหนด WMI คืออิมพลีเมนต์บน Windows ที่ PowerShell Get-CimInstance และ C# Microsoft.Management.Infrastructure เรียกตัวเองว่า «CIM» เพราะเป็น API ที่ตามมาตรฐานนี้ แต่ปลายทางที่เชื่อมคือโครงสร้าง WMI เดียวกัน สำหรับงานพัฒนาประจำ เข้าใจว่า «สอบถามคลาส WMI (เช่น Win32_*) ผ่าน API ตระกูล CIM» ก็พอใช้ในทางปฏิบัติ
- Get-WmiObject ใช้ไม่ได้อีกแล้วหรือ?
- บน Windows PowerShell 5.1 ยังรันได้ แต่ตั้งแต่ PowerShell 6 เป็นต้นไป (รวม PowerShell 7 ปัจจุบัน) cmdlet WMI v1 — Get-WmiObject, Invoke-WmiMethod, Register-WmiEvent, Set-WmiInstance และ Remove-WmiObject — ถูกลบและรันไม่ได้ ฟังก์ชันเดียวกันมีในโมดูล CimCmdlets (Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent เป็นต้น) เมื่อเขียนสคริปต์ใหม่ ปลอดภัยกว่าที่จะเขียนด้วย cmdlet CIM แม้จะรันบน 5.1 แบบนั้นพอไป PowerShell 7 จะไม่ต้องเขียนส่วน WMI ใหม่
- จาก C# ควรใช้ System.Management หรือ Microsoft.Management.Infrastructure?
- ทั้งคู่เป็น Windows เท่านั้น และจาก .NET ปัจจุบันนำเข้าเป็นแพ็กเกจ NuGet System.Management คือ API คลาสสิก ใช้ได้แค่ส่ง WQL ให้ ManagementObjectSearcher และพอเพียงถ้าเน้นดึงข้อมูลท้องถิ่น ยังมี ManagementDateTimeConverter สำหรับแปลงวันที่ DMTF ส่วน Microsoft.Management.Infrastructure (MI API) มีระบบชนิดเดียวกับ cmdlet CIM ของ PowerShell (CimSession / CimInstance) และจัดการสอบถามระยะไกลผ่าน WSMan เมธอดอะซิงโครนัส และการสมัครรับเหตุการณ์ (Subscribe) ได้ในแนวเดียวกัน หากจะใส่การสอบถามและเฝ้าพีซีระยะไกลลงผลิตภัณฑ์จริง การเลือก MI API สมเหตุสมผลกว่า
- Get-CimInstance ต่อพีซีระยะไกลไม่ได้ ควรตรวจอะไร?
- ก่อนอื่นตรวจว่า WinRM ถูกตั้งบนเครื่องปลายทางแล้วหรือยัง การปฏิบัติการ CIM ที่ระบุ -ComputerName สร้างเซสชันชั่วคราวด้วยโปรโตคอล WSMan (WinRM) จึงสมมติว่าบริการ WinRM และตัวฟังรันอยู่บนปลายทาง winrm quickconfig ทำการตั้งค่าเริ่มต้น (เริ่มบริการ สร้างตัวฟัง เพิ่มข้อยกเว้นไฟร์วอลล์) พอร์ตเริ่มต้นคือ 5985 สำหรับ HTTP และ 5986 สำหรับ HTTPS จึงตรวจไฟร์วอลล์ตามเส้นทางด้วย ในเวิร์กกรุ๊ปไม่มีการยืนยันตัวตนร่วมด้วย Kerberos อาจต้องลงทะเบียนปลายทางใน TrustedHosts ของฝั่งลูกข่าย หากตั้ง WinRM บนปลายทางไม่ได้จริง ๆ ยังต่อด้วย DCOM ได้โดยใช้ออปชันจาก New-CimSessionOption -Protocol Dcom
- ทำไมวันที่ WMI กลับมาเป็นรูปแบบอย่าง «20260801100000.000000+540»?
- เพราะวันที่ WMI ถูกเก็บเป็นสตริงตามข้อกำหนด CIM ของ DMTF (yyyymmddHHMMSS.mmmmmm±UUU ค่าท้ายคือออฟเซ็ตจาก UTC เป็นนาที) หากอ่านค่าดิบด้วย Get-WmiObject เก่าหรือ System.Management จะได้สตริงนี้ตามที่เป็น ใน C# (System.Management) ManagementDateTimeConverter มีเมธอดแปลงระหว่างรูปแบบ DMTF กับ DateTime / TimeSpan จึงใช้ตัวนั้น อย่าตัดแปะสตริงเอง ทั้งนี้ถ้าดึงผ่าน API ตระกูล CIM เช่น Get-CimInstance คุณสมบัติวันที่ถูกแปลงเป็น DateTime แล้ว จึงไม่เจอปัญหานี้ตั้งแต่ต้น