ใช้ WMI/CIM จาก C# และ PowerShell — คู่มือปฏิบัติการดึงข้อมูลฮาร์ดแวร์ เฝ้าโพรเซส และสอบถามระยะไกล

· · Windows, C#, .NET, PowerShell, WMI, CIM, แอปธุรกิจ, การพัฒนา Windows

«อยากโชว์หมายเลขซีเรียลกับชื่อรุ่นของพีซีบนหน้าจอแอปธุรกิจ» «อยากเฝ้าพื้นที่ว่างดิสก์ของเซิร์ฟเวอร์แล้วเตือน» «อยากตรวจจับว่าโพรเซสหนึ่งเริ่มทำงาน» «อยากสอบถามสถานะพีซีที่อยู่ที่อื่นรวมในที่เดียว» — ข้อกำหนดแบบนี้เป็นของประจำตอนสร้างแอปธุรกิจและเครื่องมือจัดการบน Windows และคำตอบมาตรฐานคือ WMI (Windows Management Instrumentation) หรือในชื่อมาตรฐานคือ CIM (Common Information Model)

ข้อกำหนดประจำกับ WMI/CIMคำตอบมาตรฐานต่อข้อกำหนดประจำของแอปธุรกิจ คือการแสดงหมายเลขซีเรียลกับชื่อรุ่น การเฝ้าพื้นที่ว่างดิสก์ การตรวจจับการเริ่มโพรเซส และการสอบถามพีซีระยะไกล คือ WMI หรือในชื่อมาตรฐานคือ CIMหมายเลขซีเรียล・ชื่อรุ่นWMI(ชื่อมาตรฐานคือ CIM)เฝ้าพื้นที่ว่างดิสก์ตรวจจับการเริ่มโพรเซสสอบถามพีซีระยะไกล

ภาพ 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
ความสัมพันธ์ระหว่างมาตรฐาน CIM กับอิมพลีเมนต์ WMIมาตรฐาน CIM ที่ DMTF กำหนดและดูแลถูกใช้ในกรอบโครงการ WBEM อิมพลีเมนต์ของ Microsoft คือ WMI รุ่นถัดไป MI เข้ากันได้เต็มกับ WMI เดิม และปลายทางของ API ตระกูล CIM คือโครงสร้าง WMI เดียวกันDMTF กำหนดและดูแลCIM(โมเดลมาตรฐานอุตสาหกรรม)WBEM(โครงการอุตสาหกรรม)WMI(อิมพลีเมนต์ของ Microsoft)MI(รุ่นถัดไป・เข้ากันได้เต็ม)API ตระกูล CIM(PowerShell / C#)

ภาพ 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 ได้ ไม่ใช่กลไกที่ทำได้ทุกอย่าง

โครงสร้างการสอบถาม WMIคิวรี WQL มุ่งไปคลาส Win32_* ในเนมสเปซ root/CIMV2 โปรไวด์เดอร์ที่ให้เนื้อหาของคลาสถาม OS ณ เวลานั้นแล้วสร้างค่า แล้วคืนผลลัพธ์สอบถามด้วย WQLเนมสเปซ root/CIMV2คลาส Win32_*โปรไวด์เดอร์ถาม OS ณ เวลานั้นคืนผลลัพธ์

ภาพ 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

การเรียกเมธอดของ CimInstanceCimInstance ที่ Get-CimInstance คืนมีคุณสมบัติวันที่แปลงเป็น DateTime แล้ว แต่ไม่มีเมธอดโดยตรง จึงเรียกเมธอดโดยส่งอินสแตนซ์ให้ Invoke-CimMethodGet-CimInstanceอ็อบเจกต์ CimInstanceวันที่แปลงเป็น DateTime แล้วไม่มีเมธอดโดยตรงส่งให้ Invoke-CimMethodเรียกเมธอด

ภาพ 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»

เหตุที่ควรเขียนสคริปต์ใหม่ด้วย CIMสคริปต์ที่เขียนด้วย cmdlet WMI รันบน 5.1 ได้แต่ถูกลบตั้งแต่ PowerShell 6 จึงต้องเขียนใหม่ตอนย้าย ส่วน cmdlet CIM ใช้บน 5.1 ได้ด้วย จึงเขียนของใหม่ฝั่ง CIM จะไม่เหลือต้นทุนการย้ายcmdlet WMIcmdlet CIMสคริปต์ที่เขียนใหม่เขียนด้วยอันใด?รันได้บน 5.1ใช้ได้บน 5.1 ด้วยถูกลบใน PowerShell 7ต้องเขียนใหม่ตอนย้ายไม่เหลือต้นทุนการย้าย

ภาพ 5: เขียนของใหม่ด้วย cmdlet CIM แล้วพอไป PowerShell 7 จะไม่ต้องเขียนใหม่

4. การสอบถามระยะไกล — เซสชัน CIM (WSMan เป็นค่าเริ่มต้น) และตัวเลือก DCOM

cmdlet CIM ถ้าไม่ระบุปลายทางจะต่อ WMI ท้องถิ่นด้วย COM หากระบุ -ComputerName จะสร้าง เซสชันชั่วคราวด้วยโปรโตคอล WSMan (WinRM) เพื่อเชื่อม เมื่อจะทำหลายปฏิบัติการกับคอมพิวเตอร์เครื่องเดิม การสร้างเซสชัน CIM แล้วนำกลับมาใช้ซ้ำดีกว่าด้านประสิทธิภาพ3

การเลือกวิธีเชื่อม CIMไม่ระบุคือเชื่อม WMI ท้องถิ่นด้วย COM การระบุ ComputerName สร้างเซสชันชั่วคราว WSMan ทุกครั้งที่สอบถาม หลายปฏิบัติการกับปลายทางเดิมใช้ New-CimSession ซ้ำได้เปรียบด้านประสิทธิภาพ และปลายทางที่ยังไม่ตั้ง WinRM มีตัวเลือกโปรโตคอล DCOMไม่มีมีครั้งเดียวหลายครั้งรัน cmdlet CIMระบุ ComputerName?ต่อ WMI ท้องถิ่นด้วย COMหลายปฏิบัติการกับเครื่องเดิม?เซสชันชั่วคราว WSManใช้ New-CimSession ซ้ำถูกสร้างทุกครั้งที่สอบถามปลายทางที่ยังไม่ตั้ง WinRMตัวเลือกโปรโตคอล 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 เป็นค่าเริ่มต้น
การตรวจข้อสมมติของการสอบถามระยะไกลบนปลายทาง winrm quickconfig ตั้งบริการให้เริ่มอัตโนมัติ สร้างตัวฟัง HTTP และลงทะเบียนข้อยกเว้นไฟร์วอลล์ในครั้งเดียว ตัวฟัง HTTPS ต้องเตรียมใบรับรองแล้วตั้งแยก และในเวิร์กกรุ๊ปอาจต้องลงทะเบียน TrustedHostswinrm quickconfigตั้งบริการให้เริ่มอัตโนมัติสร้างตัวฟัง HTTP(5985)ข้อยกเว้นไฟร์วอลล์ตัวฟัง HTTPS(5986)เตรียมใบรับรองแล้วตั้งแยกสภาพแวดล้อมเวิร์กกรุ๊ปลงทะเบียน 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 คือกับดักแรกคลาสสิกตรงนี้

กับดักของการอ่านคุณสมบัติแล้วแคสต์คุณสมบัติของ System.Management คืนเป็น object ผ่านอินเด็กเซอร์ จึงต้องตรวจชนิด CIM จากเอกสารแล้วแคสต์ หากคิดว่าเป็น int แล้วแคสต์จะได้ InvalidCastExceptionอ่านด้วยอินเด็กเซอร์คืนเป็น objectตรวจชนิด CIM ในเอกสารแคสต์เป็นชนิดที่ถูกคิดว่าเป็น int แล้วแคสต์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# แล้วรับผลเป็นอ็อบเจกต์» ด้วย

ไหลจากการทดลองใน PowerShell ไปคัดลอกเป็น C#cmdlet CIM ของ PowerShell กับ MI API ของ C# ใช้ชนิด CimInstance เดียวกัน จึงทำให้ไหลการพัฒนาที่ทดลองใน PowerShell แล้วคัดลอกไป C# ต่อกันตามธรรมชาติทดลองใน PowerShellcmdlet CIMอิมพลีเมนต์จริงใน C#MI APIชนิด CimInstance เดียวกันคัดลอกต่อได้ตามธรรมชาติ

ภาพ 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 ก็ได้ฐานของการเฝ้าดิสก์โดยไม่มีเอเจนต์

ฐานการเฝ้าดิสก์โดยไม่มีเอเจนต์การกรอง DriveType 3 ตัดสื่อถอดได้ ไดรฟ์เครือข่าย และซีดี เหลือเฉพาะดิสก์ท้องถิ่น แล้วนำสคริปต์เดียวกันไปแต่ละเซิร์ฟเวอร์ผ่านเซสชัน CIM จึงได้ฐานการเฝ้าดิสก์โดยไม่มีเอเจนต์สคริปต์ดึงพื้นที่ว่างกรอง DriveType = 3ตัดสื่อถอดได้ ฯลฯผ่านเซสชัน CIMส่งไปแต่ละเซิร์ฟเวอร์เฝ้าโดยไม่มีเอเจนต์

ภาพ 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

ไหลการสมัครรับเหตุการณ์การเริ่มโพรเซสเซสชัน PowerShell ที่ยกสิทธิ์ผู้ดูแลลงทะเบียนการสมัครรับด้วย Register-CimIndicationEvent เหตุการณ์เข้าทุกครั้งที่โพรเซสเริ่มแล้วรัน Action และเมื่อจบการเฝ้าจึงยกเลิกด้วย Unregister-EventWMIเซสชัน PowerShellWMIเซสชัน PowerShellรันด้วยสิทธิ์ผู้ดูแลลงทะเบียนสมัครรับด้วย Register-CimIndicationEventเหตุการณ์เข้าทุกครั้งที่โพรเซสเริ่มรัน -Actionยกเลิกด้วย Unregister-Event(เมื่อจบการเฝ้า)

ภาพ 11: การสมัครรับมีผลตลอดที่เซสชันที่ลงทะเบียนยังมีชีวิต และการยกเลิกทำเมื่อจบการเฝ้า

อีกวิธีคือ เหตุการณ์สร้างอินสแตนซ์ ทั่วไป (__InstanceCreationEvent) ที่ใช้กับคลาสใดก็ได้ ฝั่งนี้ WMI พอลลิงตามช่วงที่ระบุด้วย WITHIN แล้วทำให้ส่วนต่างเป็นเหตุการณ์ ดังนั้นคุณต้องตัดสินการแลกระหว่างช่วงตรวจจับกับภาระเอง

สองวิธีสมัครรับเพื่อตรวจจับการเริ่มโพรเซสWin32_ProcessStartTrace คือวิธีสมัครรับคลาสเหตุการณ์ของโปรไวด์เดอร์เคอร์เนลเทรซ ส่วน __InstanceCreationEvent ทั่วไปให้ WMI พอลลิงตามช่วง WITHIN แล้วทำให้ส่วนต่างเป็นเหตุการณ์ จึงต้องตัดสินการแลกช่วงเวลากับภาระเองตรวจจับการเริ่มโพรเซสWin32_ProcessStartTrace__InstanceCreationEventสมัครรับเคอร์เนลเทรซพอลลิงตามช่วง WITHINแลกช่วงเวลากับภาระ

ภาพ 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

หากจะใส่เข้าส่วนเฝ้าที่รันต่อเนื่อง ให้รวมการลงทะเบียนใหม่เมื่อการสมัครรับขาด (ตอนบริการรีสตาร์ตหรือตอนผิดพลาด) ไว้ในการออกแบบด้วย ข้อคิดด้านการออกแบบของ «การตรวจและแสดงสถานะ» รวมการเฝ้าอุปกรณ์ อยู่ใน «แนวปฏิบัติการตรวจและแสดงสถานะอุปกรณ์ภายนอก»

วงจรชีวิตการสมัครรับของการเฝ้าแบบรันต่อเนื่องในการเฝ้าแบบรันต่อเนื่อง สถานะกำลังสมัครรับอาจขาดเมื่อบริการรีสตาร์ตหรือมีข้อผิดพลาด จึงต้องรวมการออกแบบที่ตรวจว่าขาดแล้วลงทะเบียนใหม่จนกลับไปกำลังสมัครรับบริการรีสตาร์ต・ข้อผิดพลาดกำลังสมัครรับการสมัครรับขาดแล้วลงทะเบียนใหม่

ภาพ 13: การเฝ้าแบบรันต่อเนื่องต้องรวมการออกแบบที่ลงทะเบียนใหม่เมื่อการสมัครรับขาด แล้วกลับไปกำลังสมัครรับ

7. กับดัก — ประสิทธิภาพ สิทธิ์ 64bit รีโพสิทอรี และวันที่

7.1. SELECT * กับพอลลิงที่ถี่เกินไป

คิวรี WMI คือ «โปรไวด์เดอร์สร้างค่า ณ เวลานั้น» — ไม่ได้ฟรี มีแอนติแพตเทิร์นคลาสสิกสองอย่าง

  • การใช้ SELECT * ตามเคย การดึงทุกคุณสมบัติทุกแถวของ Win32_Process ทำให้ภาระของโปรไวด์เดอร์และการส่งเครือข่าย (เมื่อเป็นระยะไกล) พองตามนั้น กรองแถวด้วย -Filter กรองคอลัมน์ด้วย -Property และใช้ -KeyOnly ถ้าต้องการแค่คีย์สำหรับขั้นถัดไป ทั้งหมดนี้เป็นเครื่องมือที่ทางการจัดไว้ «เพื่อลดขนาดอ็อบเจกต์และทราฟฟิกเครือข่าย»3
  • พอลลิงช่วงสั้น ดีไซน์อย่าง «Get-CimInstance Win32_Process ทุกวินาที» ควรแทนด้วยการสมัครรับเหตุการณ์จากหัวข้อ 6.3 แม้ต้องใช้แบบพอลลิง (WITHIN) ก็ขยายช่วงให้พอเพียงต่อข้อกำหนดจริง

นอกจากนี้ การวน -ComputerName ทีละเครื่องกับปลายทางระยะไกลก็สิ้นเปลือง เพราะเซสชันชั่วคราวถูกสร้างทุกคิวรี — เปลี่ยนเป็น ใช้เซสชัน CIM ซ้ำ สำหรับหลายปฏิบัติการ3

แอนติแพตเทิร์นด้านประสิทธิภาพและจุดที่แทนการใช้ SELECT ดอกจันตามเคยให้กรองแถวและคอลัมน์ด้วย Filter กับ Property ถ้าต้องการแค่คีย์ใช้ KeyOnly พอลลิงช่วงสั้นให้แทนด้วยการสมัครรับเหตุการณ์ และการวน ComputerName ทีละเครื่องให้ใช้เซสชัน CIM ซ้ำใช้ SELECT * ตามเคยกรองด้วย Filter และ Propertyถ้าต้องการแค่คีย์ใช้ KeyOnlyพอลลิงช่วงสั้นแทนด้วยการสมัครรับเหตุการณ์ComputerName ทีละเครื่องใช้เซสชัน CIM ซ้ำ

ภาพ 14: การกรองแถว คอลัมน์ และคีย์ กับการแทนพอลลิงด้วยการสมัครรับเหตุการณ์ ป้องกันปัญหาประสิทธิภาพของ WMI ได้ประมาณครึ่งหนึ่ง

7.2. สิทธิ์ของการสมัครรับเหตุการณ์

ตามหัวข้อ 6.3 การสมัครรับตระกูล Win32_ProcessStartTrace สมมติสิทธิ์ผู้ดูแล7 «บนเครื่องพัฒนา (รันเป็นผู้ดูแล) ใช้ได้ แต่การเฝ้าไม่ทำงานในสภาพแวดล้อมผู้ใช้ทั่วไปของลูกค้า» คืออุบัติเหตุคลาสสิก คู่กับไดอะล็อกแจ้งไฟร์วอลล์ หากจะใส่การเฝ้าในแอปธุรกิจที่รันเป็นผู้ใช้ทั่วไป พิจารณาแยกส่วนเฝ้าออกเป็นบริการ Windows (รันเป็น LocalSystem เป็นต้น) แล้วเชื่อมกับตัวแอปผ่านการสื่อสารข้ามโพรเซส

โครงเฝ้าในสภาพแวดล้อมผู้ใช้ทั่วไปการสมัครรับที่สมมติสิทธิ์ผู้ดูแลถูกตัดออกจากตัวแอปที่รันเป็นผู้ใช้ทั่วไป แยกส่วนเฝ้าเป็นบริการ Windows ที่รันเป็น LocalSystem ฯลฯ แล้วเชื่อมกับตัวแอปด้วยการสื่อสารข้ามโพรเซสการสื่อสารข้ามโพรเซสบริการ Windows สำหรับเฝ้าสมัครรับเทรซการเริ่มรันเป็น LocalSystem ฯลฯตัวแอป(ผู้ใช้ทั่วไป)

ภาพ 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»

การเลือกโปรไวด์เดอร์บนสภาพแวดล้อม 64bitโดยค่าเริ่มต้นโปรไวด์เดอร์ที่ตรงกับจำนวนบิตของแอปที่เรียกเป็นผู้ตอบ การสอบถามรีจิสทรีจากแอป 32bit ได้ค่าฝั่ง Wow6432Node แต่ระบุ __ProviderArchitecture แล้วขอวิวฝั่งตรงข้ามอย่างชัดได้32bit64bitบิตของตัวเรียกคือ?โปรไวด์เดอร์ 32bit ตอบโปรไวด์เดอร์ 64bit ตอบรีจิสทรีเป็นค่าฝั่ง Wow6432Nodeระบุ __ProviderArchitectureขอวิวฝั่งตรงข้ามอย่างชัด

ภาพ 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

ขั้นตอนแยกสาเหตุเมื่อรีโพสิทอรี WMI ไม่สอดคล้องเมื่อมีข้อผิดพลาดอย่างหาคลาสไม่เจอ ให้ตรวจความสอดคล้องด้วย winmgmt verifyrepository หากไม่สอดคล้องให้สร้างใหม่ด้วย salvagerepository และอย่าลบหรือรีเซ็ตรีโพสิทอรีเป็นก้าวแรกใช่ไม่ใช่ข้อผิดพลาดเช่นหาคลาสไม่เจอwinmgmt /verifyrepositoryผลคือ inconsistent?winmgmt /salvagerepositoryสงสัยสาเหตุส่วนอื่นของ OSเนื้อหาที่อ่านได้ถูกรวมลบหรือรีเซ็ตรีโพสิทอรีอย่าใช้เป็นก้าวแรก

ภาพ 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 ในคำนวณได้เลย
การจัดการรูปแบบวันที่ DMTFวันที่ WMI ถูกเก็บเป็นสตริงรูปแบบ DMTF หากอ่านค่าดิบด้วย System.Management ให้แปลงด้วย ManagementDateTimeConverter ส่วน API ตระกูล CIM คืน DateTime ที่แปลงแล้ว จึงอย่าตัดแปะสตริงเองSystem.ManagementAPI ตระกูล CIMสตริงรูปแบบ DMTFดึงด้วย API ใด?ManagementDateTimeConverterคืนเป็น DateTime ที่แปลงแล้วแปลงเป็น DateTime / TimeSpanตัดแปะสตริงเองอย่าใช้

ภาพ 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 โดยไม่แตะตรง»

แกนการตัดสินเครื่องมือในพื้นที่ที่มีกลไกเฉพาะให้ใช้กลไกเฉพาะ และในพื้นที่ที่ไม่มี ให้ใช้ WMI กับ CIM สำหรับการสอบถามข้ามขอบเขตและการสอบถามระยะไกล โดย cmdlet CIM เฉพาะงานเป็นรูปที่รับประโยชน์โดยไม่แตะ WMI กับ CIM โดยตรงมีไม่มีมีกลไกเฉพาะอยู่แล้ว?ใช้กลไกเฉพาะใช้ WMI / CIMสอบถามข้ามขอบเขต・ระยะไกลcmdlet CIM เฉพาะงานรับประโยชน์โดยไม่แตะตรง

ภาพ 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 ไว้สำหรับการสอบถามข้ามขอบเขตและการสอบถามระยะไกล ประโยคเดียวนี้สรุปที่ยืนของมัน

บทความที่เกี่ยวข้อง

ด้านให้คำปรึกษาที่เกี่ยวข้อง

KomuraSoft LLC รับการฝังการดึงข้อมูลฮาร์ดแวร์ การเฝ้าโพรเซส และการสอบถามพีซีระยะไกลด้วย WMI/CIM เข้าแอปธุรกิจ การย้ายสคริปต์ในองค์กรที่อยู่บน Get-WmiObject ไป cmdlet CIM และการสอบสวนสาเหตุอย่าง «บนเครื่องพัฒนาใช้ได้ แต่ที่ลูกค้าติดข้อผิดพลาดสิทธิ์» ให้ปรึกษาเป็นเส้นเดียวตั้งแต่ทดลองใน PowerShell จนถึงอิมพลีเมนต์จริงใน C# ได้

ลิงก์อ้างอิง

  1. 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

  2. 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

  3. 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

  4. Microsoft Learn, New-CimSessionOption (CimCmdlets). ว่าด้วยออปชันเซสชัน CIM ที่มีสองชุดพารามิเตอร์ สำหรับ WsMan และสำหรับ DCOM ว่าด้วย -Protocol ที่รับ Dcom / Default / Wsman ว่าด้วยตัวอย่างการส่งออปชันที่สร้างด้วย New-CimSessionOption -Protocol Dcom เข้า -SessionOption ของ New-CimSession เพื่อสร้างเซสชัน CIM แบบ DCOM และว่าด้วยระดับการปลอมตัวเริ่มต้นของเซสชัน DCOM ที่เป็น Impersonate  2

  5. Microsoft Learn, ManagementObjectSearcher Class (System.Management). ว่าด้วยคลาสทางเข้าที่ใช้บ่อยที่สุดในการดึงข้อมูลการจัดการ โดยดึงคอลเลกชันของอ็อบเจกต์การจัดการตามคิวรี WQL ที่ระบุ ว่าด้วยการรับ ObjectQuery และ ManagementScope (เนมสเปซ WMI) แล้วคืน ManagementObjectCollection ผ่าน Get() และว่าด้วย System.Management.dll ที่ให้เป็นแพ็กเกจ NuGet System.Management  2 3 4

  6. 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

  7. Microsoft Learn, Register-CimIndicationEvent (CimCmdlets). ว่าด้วยการสมัครรับอินดิเคชัน (เหตุการณ์) ด้วยชื่อคลาสหรือนิพจน์คิวรี และการตั้งชื่อการสมัครรับด้วย -SourceIdentifier ว่าด้วยตัวอย่างการสมัครรับ Win32_ProcessStartTrace พร้อมหมายเหตุว่าต้องรัน PowerShell ในฐานะผู้ดูแล ว่าด้วยตัวอย่างการอ้าง ProcessName / ProcessId จาก $Event.SourceEventArgs.NewEvent ในบล็อกสคริปต์ -Action ว่าด้วยการเชื่อมผ่านเซสชันชั่วคราว WsMan เมื่อระบุ -ComputerName และการเชื่อมท้องถิ่นด้วย COM เมื่อไม่ได้ระบุ และว่าด้วยการใช้ Unregister-Event เพื่อยกเลิกการสมัครรับ  2 3 4

  8. Microsoft Learn, Win32_ProcessStartTrace class. ว่าด้วยคลาสเหตุการณ์ที่บ่งชี้การเริ่มโพรเซสใหม่ มีคุณสมบัติอย่าง ProcessName / ProcessID / ParentProcessID / SessionID / Sid ว่าด้วยคุณสมบัติ SECURITY_DESCRIPTOR ที่เป็นตัวอธิบายซึ่งโปรไวด์เดอร์เหตุการณ์ใช้ตัดสินว่าผู้ใช้ใดรับเหตุการณ์ได้ และว่าด้วยเนมสเปซที่เป็น Root\CIMV2 ให้โดยโปรไวด์เดอร์เคอร์เนลเทรซ (Krnlprov.dll)  2 3

  9. 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

  10. 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

  11. Microsoft Learn, System.Management Namespace. ว่าด้วยเนมสเปซที่สอบถามโครงสร้าง WMI ผ่านตระกูลคลาส ManagementObjectSearcher และจัดการการสมัครรับเหตุการณ์ผ่าน ManagementEventWatcher ว่าด้วย WqlEventQuery ที่แทนคิวรีเหตุการณ์รูปแบบ WQL และว่าด้วย ManagementDateTimeConverter ที่ให้เมธอดแปลงระหว่างวันที่/เวลาและช่วงเวลาแบบ DMTF กับ DateTime / TimeSpan ของ CLR  2 3

  12. 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

  13. 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 ทุกคร...

หน้าเหล่านี้วางหัวข้อไว้ในบริบทที่กว้างขึ้นของบริการและการตัดสินใจ

บทความนี้เกี่ยวข้องโดยตรงกับบริการต่อไปนี้

คำถามที่พบบ่อย

คำถามที่มักพบในการปรึกษาเกี่ยวกับหัวข้อของบทความนี้

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 แล้ว จึงไม่เจอปัญหานี้ตั้งแต่ต้น

โปรไฟล์ผู้เขียน

หน้าแนะนำผู้เขียนบทความ

Go Komura

ผู้แทนของ KomuraSoft LLC

เชี่ยวชาญด้านการพัฒนาซอฟต์แวร์ Windows ที่ปรึกษาเทคนิค และการตรวจสอบบั๊ก โดยเฉพาะโปรเจกต์ที่มีระบบเดิมและบั๊กที่ทำซ้ำได้ยาก

ลิงก์สาธารณะ

กลับไปยังบล็อก