WMI/CIM מ-C# ומ-PowerShell — מדריך מעשי למידע חומרה, ניטור process ושאילתות remote
· עודכן בתאריך: · Go Komura · Windows, C#, .NET, PowerShell, WMI, CIM, אפליקציות עסקיות, פיתוח Windows
היסטוריית עדכונים (גרסה ראשונה, פורסמה בתאריך 1 Aug 2026)
- פרסום ראשון
לצטט את המאמר הזה(DOI: 10.5281/zenodo.22175752)
מאמר זה מאוחסן בארכיון Zenodo. להלן גם ה-DOI שתמיד מפנה לגרסה האחרונה וגם ה-DOI המקובע לגרסה שאתם קוראים.
Go Komura (2026). WMI/CIM מ-C# ומ-PowerShell — מדריך מעשי למידע חומרה, ניטור process ושאילתות remote. KomuraSoft LLC. https://doi.org/10.5281/zenodo.22175752 https://comcomponent.com/he/blog/wmi-cim-practical-guide/
- DOI (הגרסה האחרונה)
- 10.5281/zenodo.22175752
- DOI (הגרסה הזו)
- 10.5281/zenodo.22175753
“רוצים להציג serial number ושם דגם של המחשב במסך של אפליקציה עסקית.” “רוצים לנטר דיסק פנוי בשרת ולהרים אזהרה.” “רוצים לזהות ש-process מסוים התחיל.” “רוצים לשלוף במרוכז את מצב המחשבים במקום remote.” — דרישות מהסוג הזה עולות שוב ושוב בפיתוח אפליקציות עסקיות וכלי ניהול ל-Windows. והתשובה הסטנדרטית להן היא WMI (Windows Management Instrumentation), או, בשם התקן, CIM (Common Information Model).
flowchart TB
accTitle: דרישות קלאסיות ו-WMI/CIM
accDescr: התשובה הקלאסית לדרישות הקלאסיות של אפליקציה עסקית — הצגת serial number ושם דגם, ניטור דיסק פנוי, זיהוי process שהתחיל ושאילתת מחשב remote — היא WMI, ובשם הסטנדרטי CIM
r1["serial number ושם דגם"] --> ans["WMI (השם הסטנדרטי: CIM)"]
r2["ניטור דיסק פנוי"] --> ans
r3["זיהוי process שהתחיל"] --> ans
r4["שאילתת מחשב remote"] --> ans
איור 1: התשובה הקלאסית לארבע הדרישות הקלאסיות של אפליקציה עסקית היא WMI/CIM.
מה שמסבך הוא שמידע על WMI מערבב ישן וחדש. חיפוש מעלה זה לצד זה מאמרים בני עשר שנים שמשתמשים ב-Get-WmiObject ומאמרים שמשתמשים ב-Get-CimInstance, ובצד C# יש שתי שושלות נפרדות: System.Management ו-Microsoft.Management.Infrastructure. קשה לדעת מהי הכתיבה הנוכחית ומה “עדיין רץ אבל לא בוחרים לקוד חדש”. בפועל Get-WmiObject כלל לא קיים ב-PowerShell 7, והעובדה הזאת צצה בבת אחת כשמגרים סקריפט פנימי שנכתב ל-5.1.
המאמר הזה מיועד למפתחי C#/PowerShell שמיישמים באפליקציה עסקית שליפת מידע חומרה, ניטור process ושאילתות מחשב remote. הוא מסדר, מעוגן במקורות ראשוניים נכון לאוגוסט 2026, את ההבנה המינימלית של מבנה WMI/CIM, את CIM cmdlets של PowerShell, את שני ה-API ב-C#, מתכונים נפוצים בפועל, מלכודות של ביצועים, הרשאות ו-64bit, ולבסוף איך לשפוט “מתי לא להשתמש ב-WMI”.
1. קודם כל, המסקנות
- CIM הוא מודל התקן התעשייתי למידע ניהול שהוגדר ב-DMTF, ו-WMI הוא המימוש של Microsoft. API ממשפחת “CIM” ב-PowerShell וב-C# הם דור נוכחי שעוקב אחרי התקן, והם מתחברים לאותה תשתית WMI.1
- ב-PowerShell הדור הנוכחי הוא CIM cmdlets (Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent). cmdlets ישנים של WMI (Get-WmiObject ועוד ארבעה) הוסרו מ-PowerShell 6 ואילך ואינם רצים ב-PowerShell 7.2
- ה-namespace שברירת המחדל הוא root/CIMV2, ושאילתה יום-יומית היא בעיקרה סינון classes מסוג Win32_* שם ב-WQL.3
- שאילתה remote ברירת המחדל שלה WSMan (WinRM). ציון
-ComputerNameיוצר session זמני של WSMan. אם שואלים את אותו יעד שוב ושוב, שימוש חוזר ב-CIM session (New-CimSession) הוא הנוהג המבוסס לביצועים, וליעד ישן שאי אפשר להגדיר בו WinRM יש אפשרות פרוטוקול DCOM.34 - ב-C# יש שתי שושלות: System.Management (ManagementObjectSearcher) ו-Microsoft.Management.Infrastructure (CimSession). שתיהן ייעודיות ל-Windows, וב-.NET הנוכחי מביאים אותן מ-NuGet. אם בונים שאילתות remote או ניטור למוצר של ממש, MI API — שחולק את מערכת הטיפוסים עם CIM cmdlets — מתאים יותר.56
- זיהוי process שהתחיל נעשה ב-event subscription, לא ב-polling. Subscribe ל-
Win32_ProcessStartTraceחייב לרוץ בהרשאות Administrator.78 - לא משתמשים ב-
SELECT *מתוך הרגל. צמצום מה שמועבר עם-Filter/-Property/-KeyOnlyמונע בערך מחצית מבעיות הביצועים של WMI.3 - WMI אינו פתרון אוניברסלי. לניטור ביצועים בתדירות גבוהה, לקריאה וכתיבה של הגדרות האפליקציה עצמה, או לקריאה חד-פעמית לפונקציית מערכת, performance counters, Registry, Win32 API או cmdlets ייעודיים מתאימים יותר (ראו טבלת ההחלטה בפרק 8).
ב-diagram, solid line מציינת relation שתמיד מתקיים ו-dashed line מציינת relation מותנה (התנאים מופיעים בהסבר של כל relation ב-detail page). הרשימה המלאה של ה-relations (סה”כ 26, כולל evidence ו-certainty) וההגדרות של ה-concepts המרכזיים נמצאות ב-detail page של ה-knowledge map (ביפנית). Data: JSON-LD / Turtle
2. מהו WMI/CIM — תקן מול מימוש, namespaces, classes ו-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 המסורתי, ורוב ה-providers החדשים נכתבים ב-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: היררכיה שאוספת classes. לשאילתה יום-יומית משתמשים כמעט תמיד ב-root/CIMV2, שהוא גם ברירת המחדל של CIM cmdlets.3 אחרים כוללים את
root\default(Registry provider ועוד). - class: טיפוס שמייצג יעד ניהול, כמו
Win32_ComputerSystem(המחשב עצמו),Win32_LogicalDisk(כונן לוגי) אוWin32_Process(process). classes ייחודיות ל-Windows שיורשות מ-classes של תקן CIM (כמוCIM_LogicalDisk) נושאות את הקידומתWin32_.9 - provider: הרכיב שמספק את התוכן הממשי של class. כששואלים אותו, ה-provider פונה ל-OS במקום ובונה את הערכים.
- WQL: שפת שאילתה דמוית SQL. כמו ב-
SELECT Name, State FROM Win32_Service WHERE StartMode = 'Auto', מתייחסים ל-class כאל טבלה ומסננים אותה. WQL היא גם שפת השאילתה שברירת המחדל של CIM cmdlets.3
“לקרוא מידע על ה-OS ועל החומרה דרך classes ושפת שאילתה אחידות” — זה הערך ש-WMI נותן. ולהפך, כתיבה ופעולות מוגבלות ל-classes שיש להן מתודות שאפשר לקרוא ב-Invoke-CimMethod; זה לא מנגנון שיכול הכול.
flowchart TB
accTitle: מבנה שאילתת WMI
accDescr: שאילתת WQL מופנית ל-classes מסוג Win32_* ב-namespace root/CIMV2, ה-provider שמספק את תוכן ה-class פונה ל-OS במקום ובונה ערכים, והתוצאה חוזרת
wql["שאילתה ב-WQL"] --> ns["namespace root/CIMV2"]
ns --> cls["Win32_* classes"]
cls --> prov["provider"]
prov --> osq["פנייה ל-OS במקום"]
osq --> res["החזרת תוצאה"]
איור 3: השאילתה עוברת ב-namespace, ב-class וב-provider, והערכים נוצרים במקום.
3. שימוש מ-PowerShell — CIM cmdlets הם הנוכחיים, WMI cmdlets הוסרו
3.1. הבסיס: Get-CimInstance
# לפי שם class (namespace ברירת מחדל root/CIMV2)
Get-CimInstance -ClassName Win32_OperatingSystem
# כותבים ב-Filter רק את תוכן פסוקית WHERE (בלי מילת המפתח 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.
# קריאת מתודה על מופע: owner של כל process
Get-CimInstance -ClassName Win32_Process -Filter "Name = 'notepad.exe'" |
Invoke-CimMethod -MethodName GetOwner
# קריאת מתודה סטטית של ה-class: הפעלת process
Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = 'notepad.exe' }
# בדיקת הגדרת ה-class (מאפיינים ומתודות)
Get-CimClass -ClassName Win32_Process
3.2. טבלת מיגרציה מ-WMI cmdlets ישנים
מ-PowerShell 6 ואילך (כולל PowerShell 7 הנוכחי) WMI v1 cmdlets הבאים הוסרו. אותה פונקציונליות מסופקת במודול CimCmdlets (WMI v2).2
| ישן (עד Windows PowerShell 5.1) | נוכחי (CIM cmdlets) | הערות |
|---|---|---|
Get-WmiObject |
Get-CimInstance |
עקרון -Filter / -Query זהה |
Get-WmiObject -List |
Get-CimClass |
גילוי ובדיקת הגדרות class |
Invoke-WmiMethod |
Invoke-CimMethod |
ארגומנטים מועברים כ-hashtable ב--Arguments @{ } |
Register-WmiEvent |
Register-CimIndicationEvent |
event subscription (סעיף 6.3) |
Set-WmiInstance |
Set-CimInstance |
שינוי מאפיינים הניתנים לכתיבה |
Remove-WmiObject |
Remove-CimInstance |
מחיקת מופע |
CIM cmdlets רצים גם תחת Windows PowerShell 5.1, לכן כל מה שכותבים מעתה נכתב בצד CIM גם אם ירוץ תחת 5.1 — כך לא נשארת עלות מיגרציה. לתמונה המלאה של דו-קיום ומיגרציה בין 5.1 ל-7 ראו “ההבדלים בין Windows PowerShell 5.1 ל-PowerShell 7”.
flowchart TB
accTitle: למה לכתוב סקריפט חדש ב-CIM
accDescr: סקריפט שנכתב ב-WMI cmdlets רץ ב-5.1 אבל הוסר מ-PowerShell 6 ואילך ולכן דורש שכתוב במיגרציה, ו-CIM cmdlets זמינים גם ב-5.1 ולכן כתיבה חדשה בצד CIM לא משאירה עלות מיגרציה
new["סקריפט שנכתב עכשיו"] --> q1{"באיזה מהם כותבים?"}
q1 -->|WMI cmdlets| old["רץ ב-5.1"]
q1 -->|CIM cmdlets| cur["זמין גם ב-5.1"]
old --> del["הוסר ב-PowerShell 7"]
del --> rew["שכתוב במיגרציה"]
cur --> norew["אין עלות מיגרציה"]
איור 5: כתיבה חדשה ב-CIM cmdlets חוסכת שכתוב כשמגרים ל-PowerShell 7.
4. שאילתות remote — CIM session (WSMan כברירת מחדל) ואפשרות DCOM
אם לא מציינים יעד, CIM cmdlets מתחברים ל-WMI המקומי ב-COM; אם מציינים -ComputerName, הם יוצרים session זמני בפרוטוקול WSMan (WinRM) כדי להתחבר. כשמבצעים כמה פעולות מול אותו מחשב, יצירת CIM session ושימוש חוזר בו עדיפה לביצועים.3
flowchart TB
accTitle: בחירת אופן החיבור של CIM
accDescr: בלי ציון מתחברים ב-COM ל-WMI המקומי, עם ComputerName נוצר session זמני של WSMan בכל שאילתה, כמה פעולות לאותו יעד מרוויחות משימוש חוזר ב-New-CimSession, וליעד בלי WinRM יש אפשרות פרוטוקול DCOM
exec["הרצת CIM cmdlet"] --> q1{"צוין ComputerName?"}
q1 -->|אין| local["חיבור COM ל-WMI מקומי"]
q1 -->|כן| q2{"כמה פעולות לאותו יעד?"}
q2 -->|חד-פעמי| temp["session זמני של WSMan"]
q2 -->|כמה| sess["שימוש חוזר ב-New-CimSession"]
temp -.-> cost["נוצר בכל שאילתה"]
nowinrm["יעד בלי WinRM"] -.-> dcom["אפשרות פרוטוקול DCOM"]
איור 6: שאילתה remote ברירת המחדל שלה WSMan, ולכמה פעולות מול אותו יעד שימוש חוזר ב-CIM session הוא הנוהג.
# לשאילתה חד-פעמית משתמשים ב-ComputerName (session זמני נוצר בכל פעם)
Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName Server01, Server02
# לשאילתות חוזרות משתמשים שוב ב-CIM session
$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
התנאים המוקדמים לשאילתה remote הם אלה.
- WinRM חייב להיות מוגדר ביעד.
winrm quickconfigמגדיר את השירות להפעלה אוטומטית, יוצר HTTP listener (פורט ברירת מחדל 5985) ורושם חריג firewall, הכול בפעם אחת.10 אם רוצים להתחבר ב-HTTPS (פורט ברירת מחדל 5986), זה לבדו לא מספיק — צריך להכין certificate של שרת ולהגדיר בנפרד HTTPS listener, למשל ב-winrm quickconfig -transport:https.10 - הפורטים הרלוונטיים חייבים להיות פתוחים ב-firewall בדרך. עבודת התכנון והרישום של inbound rules מכוסה במאמר “Windows Firewall ואפליקציות עסקיות”.
- אימות. בסביבת domain, Kerberos מספק mutual authentication. ב-workgroup אין Kerberos, וייתכן שצריך לרשום את היעד ב-
TrustedHostsשל הלקוח. מצמצמים את הרשימה ככל האפשר.10 - הרשאות. בתצורת ברירת המחדל, שאילתות ופעולות WMI remote נעשות בחשבון ששייך לקבוצת Administrators של היעד. כדי לפתוח זאת למשתמשים רגילים צריך להגדיר הרשאות גישה גם ב-WinRM וגם ב-namespace של WMI.10
- שימו לב של-DCOM אין פורט האזנה קבוע (הוא משתמש בפורטים דינמיים של RPC), ולכן קשה יותר לתכנן אותו מעבר ל-firewall. בטוח להניח WSMan כברירת מחדל לכל דבר שבונים מעתה.
flowchart TB
accTitle: אישור התנאים המוקדמים לשאילתה remote
accDescr: ביעד winrm quickconfig מגדיר הפעלה אוטומטית של השירות, יוצר HTTP listener ורושם חריג firewall בפעם אחת, HTTPS listener מוגדר בנפרד אחרי הכנת certificate, ובסביבת workgroup לעיתים נדרש רישום ב-TrustedHosts
qc["winrm quickconfig"] --> svc["הפעלה אוטומטית של השירות"]
qc --> lis["יצירת HTTP listener (5985)"]
qc --> fw["חריג firewall"]
lis ~~~ https["HTTPS listener (5986)"]
https -.-> cert["מכינים certificate ומגדירים בנפרד"]
fw ~~~ wg["סביבת workgroup"]
wg -.-> th["רישום ב-TrustedHosts"]
איור 7: winrm quickconfig מבצע את תצורת ברירת המחדל בפעם אחת; HTTPS listener ואימות ב-workgroup דורשים טיפול נפרד.
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 |
| class כניסה | ManagementObjectSearcher (מעבירים WQL לשאילתה)5 |
CimSession (Create → QueryInstances / InvokeMethod / Subscribe)6 |
| מערכת טיפוסים | ManagementObject / ManagementEventWatcher11 |
CimInstance / CimSession — אותם טיפוסים כמו CIM cmdlets3 |
| גישה remote | מבוססת DCOM | מבוססת WSMan (CIM session). גרסאות async (*Async) זמינות6 |
| מתאים ל | שליפת מידע מקומי. תחזוקת נכסי קוד קיימים | בניית שאילתות remote וניטור. תכנונים שמשלבים 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 דרך indexer, לכן צריך לבדוק בתיעוד ה-class את טיפוס CIM (בדוגמה הזאת FreeSpace / Size הם uint649) ולהמיר בהתאם. להניח שזה int ולהמיר על בסיס ההנחה, עד שמקבלים InvalidCastException, היא המעידה הקלאסית הראשונה כאן.
flowchart TB
accTitle: מלכודת שליפת מאפיין והמרה
accDescr: מאפיינים ב-System.Management חוזרים כ-object דרך indexer, לכן צריך לאשר את טיפוס CIM בתיעוד ולהמיר, והמרה מתוך הנחה שזה int גורמת ל-InvalidCastException
idx["שליפה ב-indexer"] --> obj["חוזר כ-object"]
obj --> chk["אישור טיפוס CIM בתיעוד"]
chk --> cast["המרה לטיפוס הנכון"]
obj -.-> wrong["המרה מתוך הנחה שזה int"]
wrong -.-> ex["InvalidCastException"]
איור 8: מאפיינים חוזרים כ-object, לכן מאשרים את טיפוס CIM לפני המרה.
5.2. MI API: היסודות של CimSession
CimSession מטפל בגישה מקומית ו-remote באותה צורה. יש בו מנייה, שאילתה, קריאת מתודה, Subscribe לאירועים וגרסאות async במקום אחד.6
// NuGet: Microsoft.Management.Infrastructure (Windows בלבד)
using Microsoft.Management.Infrastructure;
// CimSession.Create(null) למקומי, או שם מחשב ל-remote
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 ש-CIM cmdlets של PowerShell מחזירים, זרימת פיתוח של “מנסים ב-PowerShell ואז מעתיקים ל-C#” מתחברת באופן טבעי. אם מתכננים את שילוב C# ו-PowerShell עצמו, ראו גם “איך להריץ PowerShell מ-C# (CSharp) ולקבל את התוצאות כאובייקטים”.
flowchart TB
accTitle: ניסוי ב-PowerShell והעתקה ל-C#
accDescr: CIM cmdlets של PowerShell ו-MI API ב-C# מטפלים באותו טיפוס CimInstance, לכן זרימת פיתוח שמנסה ב-PowerShell ואז מעתיקה ל-C# מתחברת באופן טבעי
trial["ניסוי ב-PowerShell"] --> gci["CIM cmdlets"]
impl["מימוש מלא ב-C#"] --> mi["MI API"]
gci --> ci["אותו טיפוס CimInstance"]
mi --> ci
ci -.-> flow["ההעתקה מתחברת באופן טבעי"]
איור 9: CIM cmdlets ו-MI API מטפלים באותו CimInstance, לכן ניסוי מוביל למימוש מלא.
6. מתכונים נפוצים בפועל
6.1. טבלת עזר ל-classes נפוצות
| מידע רצוי | class | מאפיינים עיקריים |
|---|---|---|
| יצרן / שם דגם | Win32_ComputerSystem |
Manufacturer, Model |
| serial number של השלדה | Win32_BIOS |
SerialNumber |
| גרסת מערכת / זמן boot | Win32_OperatingSystem |
Caption, Version, LastBootUpTime |
| דיסק פנוי | Win32_LogicalDisk |
DeviceID, FreeSpace, Size, DriveType9 |
| מצב service | Win32_Service |
Name, State, StartMode |
| רשימת processes | Win32_Process |
Name, ProcessId, CommandLine |
6.2. הקלאסיקה של ניהול נכסים: serial number, שם דגם ודיסק פנוי
# מידע דגם ו-serial number (להצלבה מול ספר נכסי מחשבים)
$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 מייצג “דיסק מקומי”, ומחריג removable (2), כונני רשת (4) ו-CD (5).9 לניטור, די להריץ את הסקריפט הזה לכל שרת דרך CIM session כדי לקבל את הבסיס לניטור דיסק בלי agent.
flowchart TB
accTitle: הבסיס לניטור דיסק בלי agent
accDescr: סינון DriveType 3 מחריג removable, כונני רשת ו-CD ומשאיר רק דיסקים מקומיים, ואותו סקריפט מועבר לכל שרת דרך CIM session וכך נוצר בסיס לניטור דיסק בלי agent
scr["סקריפט שליפת שטח פנוי"] --> flt["מסננים ב-DriveType = 3"]
flt -.-> exc["מחריגים removable וכדומה"]
scr --> ses["דרך CIM session"]
ses --> srvs["מעבירים לכל שרת"]
srvs --> mon["ניטור בלי agent"]
איור 10: סקריפט שמסונן לדיסקים מקומיים ומועבר לכל שרת ב-CIM session הוא הבסיס לניטור.
6.3. זיהוי process שהתחיל — event subscription
במקום “לשלוף Win32_Process במחזור polling ולהשוות הפרשים”, משתמשים ב-event subscription. הדרך הפשוטה לתפוס process שהתחיל היא Subscribe ל-Win32_ProcessStartTrace (event class של kernel trace provider, עם מאפיינים כמו ProcessName / ProcessID / ParentProcessID8).
# להריץ מסשן PowerShell מוגבה (Administrator)
$action = {
$name = $Event.SourceEventArgs.NewEvent.ProcessName
$id = $Event.SourceEventArgs.NewEvent.ProcessID
Write-Host "process התחיל: $name (PID=$id)"
}
Register-CimIndicationEvent -ClassName Win32_ProcessStartTrace `
-SourceIdentifier ProcessStarted -Action $action
ה-subscription נשאר חי כל עוד session PowerShell שרשם אותו חי, ו--Action רץ בכל פעם ש-process מתחיל. זהירים לא להריץ את פקודת הביטול מיד אחר כך באותה אצווה — זה פשוט גורם ל-subscription להיעלם לפני שהניטור בכלל מתחיל. את שלב הביטול מריצים רק כשמסיימים את הניטור.
# כשמסיימים את הניטור: מסירים את ה-subscription
Unregister-Event -SourceIdentifier ProcessStarted
Register-CimIndicationEvent רושם subscription לפי שם class או לפי שאילתת event ב-WQL, ובלוק הסקריפט של -Action רץ בכל הגעה של event.7 Subscribe ל-class הזאת דורש הרשאות Administrator.7 מי יכול לקבל את ה-event נשלט ב-security descriptor של ה-event class, ומשתמש רגיל בלי הרשאות מוגבהות יידחה.8
sequenceDiagram
accTitle: זרימת event subscription להפעלת process
accDescr: ב-session PowerShell בהרשאות Administrator Register-CimIndicationEvent רושם subscription, בכל process שהתחיל מגיע event ו-Action רץ, ובסיום הניטור Unregister-Event מבטל
participant ps as session PowerShell
participant wmi as WMI
ps->>wmi: רישום subscription ב-Register-CimIndicationEvent
Note over ps: הרצה בהרשאות Administrator
wmi-->>ps: event מגיע בכל process שהתחיל
ps->>ps: הרצת -Action
ps->>wmi: ביטול ב-Unregister-Event (בסיום הניטור)
איור 11: ה-subscription תקף כל עוד ה-session שרשם אותו חי, והביטול נעשה כשמסיימים את הניטור.
דרך נוספת היא instance creation event הכללי (__InstanceCreationEvent), שניתן לשימוש עם כל class. כאן WMI עושה polling במרווח שמציינים ב-WITHIN והופך את ההפרש ל-event, לכן את הפשרה בין מרווח הזיהוי לעומס מחליטים בעצמכם.
flowchart TB
accTitle: שני אופני subscription לזיהוי process שהתחיל
accDescr: Win32_ProcessStartTrace הוא Subscribe ל-event class של kernel trace provider, ו-__InstanceCreationEvent הכללי הוא מנגנון שבו WMI עושה polling במרווח WITHIN והופך הפרש ל-event, לכן את הפשרה בין מרווח לעומס מחליטים בעצמכם
goal["זיהוי process שהתחיל"] --> t1["Win32_ProcessStartTrace"]
goal --> t2["__InstanceCreationEvent"]
t1 -.-> k1["Subscribe ל-kernel trace"]
t2 -.-> w1["polling במרווח WITHIN"]
w1 -.-> tr["פשרה בין מרווח לעומס"]
איור 12: Subscribe ל-event class ייעודית, או instance creation event כללי עם מרווח polling.
# ניטור מופעי Win32_Process חדשים ב-polling כל 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;
// מ-process שרץ כ-Administrator
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($"process התחיל: {name} (PID={pid})");
};
watcher.Start();
// בסיום הניטור לא שוכחים watcher.Stop() ו-Dispose
אם בונים זאת לרכיב ניטור קבוע, התכנון צריך לכלול גם רישום מחדש כשה-subscription נופל (ב-restart של service או בשגיאה). שיקולי התכנון של “בדיקה והצגה של מצב”, כולל ניטור התקנים, מכוסים ב”שיטות עבודה מומלצות לבדיקה ולהצגה של מצב התקן חיצוני”.
stateDiagram-v2
accTitle: מחזור חיי ה-subscription בניטור קבוע
accDescr: בניטור קבוע מצב ה-subscription עלול להיקטע ב-restart של service או בשגיאה, לכן כוללים בתכנון זיהוי של הניתוק, רישום מחדש וחזרה ל-subscription
s1: ב-subscription
s2: ה-subscription נקטע
s3: רישום מחדש
[*] --> s1
s1 --> s2: restart של service או שגיאה
s2 --> s3
s3 --> s1
איור 13: בניטור קבוע כוללים בתכנון רישום מחדש כשה-subscription נקטע, כדי לחזור למצב subscription.
7. מלכודות — ביצועים, הרשאות, 64bit, repository ותאריכים
7.1. SELECT * וסערת polling
שאילתת WMI היא “provider שבונה את הערכים במקום” — היא לא בחינם. יש שני דפוסי-נגד קלאסיים.
- שימוש ב-
SELECT *מתוך הרגל. שליפת כל מאפיין של כל שורה ב-Win32_Processמנפחת בהתאם את עבודת ה-provider ואת ההעברה ברשת (כש-remote). מסננים שורות ב--Filter, עמודות ב--Property, ומשתמשים ב--KeyOnlyאם כל מה שצריך הוא keys לפעולה המשך. כולם אמצעים רשמיים שנועדו “להקטין את גודל האובייקטים ואת תעבורת הרשת”.3 - polling במרווח קצר. תכנון כמו “
Get-CimInstance Win32_Processכל שנייה” מחליפים ב-event subscription מסעיף 6.3. גם אם חייבים סגנון polling (WITHIN), מרחיבים את המרווח למה שבאמת מספיק לדרישה.
גם חזרה על -ComputerName יעד-יעד מול מכונות remote בזבזנית, כי session זמני נוצר בכל שאילתה — עוברים לשימוש חוזר ב-CIM session לכמה פעולות.3
flowchart TB
accTitle: דפוסי-נגד של ביצועים ולאן מחליפים אותם
accDescr: שימוש מתוך הרגל ב-SELECT כוכבית מסננים ב-Filter ו-Property וב-KeyOnly אם צריך רק keys, polling במרווח קצר מחליפים ב-event subscription, וחזרה על ComputerName מכונה-מכונה מחליפים בשימוש חוזר ב-CIM session
a1["שימוש מתוך הרגל ב-SELECT *"] --> f1["מסננים ב-Filter ו-Property"]
f1 -.-> f2["רק keys: KeyOnly"]
a2["polling במרווח קצר"] --> f3["מחליפים ב-event subscription"]
a3["ComputerName מכונה-מכונה"] --> f4["שימוש חוזר ב-CIM session"]
איור 14: סינון שורות, עמודות ו-keys והחלפה ב-event subscription מונעים כמחצית מבעיות הביצועים של WMI.
7.2. הרשאות ל-event subscription
כמפורט בסעיף 6.3, Subscribe למשפחת Win32_ProcessStartTrace מניח הרשאות Administrator.7 “עבד במכונת הפיתוח (הרצה כ-Administrator), אבל הניטור לא עובד בסביבת המשתמש הרגיל אצל הלקוח” הוא כשל קלאסי, לצד דיאלוג ההתראה של ה-firewall. אם בונים ניטור לאפליקציה עסקית שרצה כמשתמש רגיל, שוקלים להפריד את חלק הניטור ל-Windows service (שרץ כ-LocalSystem, למשל) ולחבר אותו לאפליקציה הראשית ב-IPC.
flowchart TB
accTitle: תצורת ניטור בסביבת משתמש רגיל
accDescr: subscription שמניח הרשאות Administrator מוצא מגוף האפליקציה שרצה כמשתמש רגיל, חלק הניטור מופרד ל-Windows service שרץ כ-LocalSystem וכדומה, והגוף מחובר אליו ב-IPC
svcm["Windows service לניטור"] --> subm["Subscribe למעקב הפעלה"]
svcm -.-> lsm["רץ כ-LocalSystem וכדומה"]
appm["גוף האפליקציה (משתמש רגיל)"] ---|IPC| svcm
איור 15: subscription שדורש הרשאות Administrator מופרד לצד ה-service, וגוף האפליקציה מחובר ב-IPC.
7.3. 32bit/64bit ו-providers
ב-Windows 64bit חלק מה-providers קיימים גם בגרסת 32bit וגם בגרסת 64bit, וכברירת מחדל הגרסה שתואמת לביטיות של הקורא עונה.12 הדוגמה הקלאסית היא Registry provider (StdRegProv) תחת root\default: קריאה מאפליקציית 32bit מחזירה ערכים מצד Wow6432Node (תצוגת 32bit).12 אם “ערך Registry שנקרא דרך WMI לא תואם למה ש-regedit מראה”, זה הדבר הראשון לחשוד בו. אם צריך את התצוגה השנייה, אפשר לבקש אותה במפורש בהגדרת __ProviderArchitecture (ואם רוצים לכפות, __RequiredArchitecture) בהקשר החיבור.12 התמונה הכללית של בעיות ביטיות מכוסה גם ב”קריאה בטוחה ל-Win32 API מ-C# — מדריך מעשי ל-P/Invoke”.
flowchart TB
accTitle: בחירת provider בסביבת 64bit
accDescr: כברירת מחדל עונה ה-provider שתואם לביטיות של האפליקציה הקוראת, שאילתת Registry מאפליקציית 32bit מקבלת ערכים מצד Wow6432Node, אבל ציון __ProviderArchitecture מבקש במפורש את התצוגה השנייה
q1{"מה ביטיות הקורא?"} -->|32bit| p32["provider 32bit עונה"]
q1 -->|64bit| p64["provider 64bit עונה"]
p32 -.-> wow["Registry הוא ערך מצד Wow6432Node"]
ctx["ציון __ProviderArchitecture"] -.-> ov["בקשה מפורשת לתצוגה השנייה"]
איור 16: כברירת מחדל עונה הצד שתואם לביטיות הקורא, ולכן אפליקציית 32bit קוראת את צד Wow6432Node.
7.4. תסמינים וטיפול כש-WMI repository פגום
הגדרות ה-class של WMI מאוחסנות ב-repository (לא קובץ יחיד — הקבצים בתיקיית Repository יחד מתפקדים כמסד נתונים13). כשהוא הופך ללא-עקבי, מתחילות להופיע שגיאות כמו “class שאמורה להתקיים לא נמצאת” או “ה-namespace אינו תקף”, אף שמצד האפליקציה לא השתנה דבר. משתמשים ב-winmgmt.exe לאבחון ולתיקון.13
rem בדיקת עקביות (תוצאה inconsistent פירושה שיש בעיה)
winmgmt /verifyrepository
rem בדיקת עקביות, ובנייה מחדש אם יש בעיה (תוכן קריא ממוזג)
winmgmt /salvagerepository
החשוב הוא לא להפוך מחיקה או איפוס של ה-repository למהלך הראשון. שגיאות שעולות דרך WMI יכולות לנבוע ממקום אחר במערכת, ו-Microsoft עצמה אומרת במפורש שמחיקת ה-repository כתגובה ראשונה “עלולה לגרום לנזק למערכת או לאפליקציות מותקנות”.13 שומרים על הסדר: בדיקה ב-/verifyrepository, ואז תיקון ב-/salvagerepository.
flowchart TB
accTitle: הליך הבירור לחוסר עקביות ב-WMI repository
accDescr: כשעולה שגיאה כמו class שלא נמצאת מאשרים עקביות ב-verifyrepository של winmgmt, אם אין עקביות בונים מחדש ב-salvagerepository, ולא הופכים מחיקה או איפוס של ה-repository למהלך הראשון
sym["שגיאה כמו class שלא נמצאת"] --> verify["winmgmt /verifyrepository"]
verify --> q1{"התוצאה inconsistent?"}
q1 -->|כן| salvage["winmgmt /salvagerepository"]
q1 -->|לא| other["חשודים בגורם אחר במערכת"]
salvage -.-> merge["תוכן קריא ממוזג"]
del["מחיקה או איפוס של ה-repository"] -.-> ng["לא המהלך הראשון"]
איור 17: שומרים על הסדר verify ואז salvage, ולא הופכים מחיקה למהלך הראשון.
7.5. המרת פורמט תאריך DMTF
תאריכי WMI מאוחסנים כמחרוזות בפורמט DMTF שמוגדר במפרט CIM: yyyymmddHHMMSS.mmmmmm±UUU (הסיומת היא offset מ-UTC בדקות — למשל 20260801100000.000000+540). לא חותכים ומרכיבים את הערך הגולמי בעיבוד מחרוזות; משתמשים ב-API ההמרה.
- C# (System.Management):
ManagementDateTimeConverterמספק המרה בין פורמט DMTF ל-DateTime/TimeSpan.11 - 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 |
|---|---|---|
| שליפת מידע חומרה ותצורת מערכת, שאילתות remote בלי agent | WMI/CIM | זה בדיוק מגרש הבית של WMI — אחיד יותר מלפנות ל-API ייעודי אחד-אחד |
| קריאה וכתיבה של הגדרות האפליקציה עצמה | קריאה ישירה ל-Registry (Microsoft.Win32.Registry) או קובצי תצורה |
גישה ל-Registry דרך WMI היא דרך עקלקלה, והיא גם יורשת את בעיית הביטיות מסעיף 7.3 |
| ניטור ביצועים רציף בתדירות גבוהה כמו שימוש ב-CPU | performance counters (System.Diagnostics.PerformanceCounter וכדומה) |
ה-counters נועדו בדיוק לזה. polling של WMI במרווח קצר מפסיד גם בעומס וגם בדיוק |
| קריאה חד-פעמית לפונקציית מערכת, או עיבוד שצריך latency נמוכה | Win32 API (P/Invoke) | ל-WMI יש overhead של מעבר דרך COM/provider |
| מנייה ותפעול של processes מקומיים כשהרשאות ה-process עצמו מספיקות | System.Diagnostics.Process |
נסגר בספרייה התקנית, עם פחות תלויות |
| תצורת יכולות ניהול Windows כמו firewall או רשת | CIM cmdlets ייעודיים כמו Get-NetFirewallRule |
סט cmdlets שנבנה ומתוחזק למטרה מסוימת מדויק ובטוח יותר מחיפוש ב-classes גולמיות של WMI |
| זיהוי שינויים בקובץ או בתיקייה | FileSystemWatcher |
לא מכניסים WMI לתחום שכבר יש לו API ייעודי |
כלל האצבע פשוט: משתמשים במנגנון הייעודי במקום שיש אחד, ושומרים את WMI/CIM לשאילתות רוחביות ולשאילתות remote. הדוגמה Get-NetFirewallRule בשורה האחרונה היא, פנימית, סט cmdlets שנבנה מעל CIM — מקרה של “לקבל את התועלת של WMI/CIM בלי לגעת בו ישירות”.
flowchart TB
accTitle: ציר ההחלטה בין האמצעים
accDescr: במקום שיש מנגנון ייעודי משתמשים במנגנון הייעודי, ובמקום שאין — בשאילתה רוחבית או remote — משתמשים ב-WMI וב-CIM, ו-CIM cmdlets ייעודיים הם צורה שמקבלת את התועלת בלי לגעת ב-WMI וב-CIM ישירות
q1{"יש מנגנון ייעודי?"} -->|יש| ded["משתמשים במנגנון הייעודי"]
q1 -->|אין| wmi["משתמשים ב-WMI / CIM"]
wmi -.-> use["שאילתה רוחבית או remote"]
cmd["CIM cmdlets ייעודיים"] -.-> ben["צורה שמקבלת רק את התועלת"]
איור 19: במקום שיש מנגנון ייעודי משתמשים בייעודי, ולשאילתה רוחבית ו-remote משתמשים ב-WMI/CIM.
9. סיכום
- CIM הוא תקן התעשייה של DMTF, ו-WMI הוא המימוש של Microsoft. גם CIM cmdlets של PowerShell וגם MI API ב-C# הם נקודות כניסה מדור נוכחי שעוקבות אחרי התקן.
- ב-PowerShell, Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent הם הנוכחיים. Get-WmiObject ו-WMI cmdlets האחרים אינם קיימים ב-PowerShell 7, לכן סקריפטים חדשים נכתבים בצד CIM גם אם היעד הוא 5.1.
- שאילתה remote ברירת המחדל שלה WSMan (WinRM), ולכמה פעולות משתמשים שוב ב-CIM session. ליעד שבו WinRM אינו מוגדר, DCOM הוא נתיב מילוט זמין.
- ב-C# בוחרים בין System.Management (נוח, מוכוון מקומי) ל-Microsoft.Management.Infrastructure (מוכוון remote וניטור, עם מערכת הטיפוסים של CIM cmdlets). שתיהן חבילות NuGet ייעודיות ל-Windows.
- מנטרים processes ב-event subscription, לא ב-polling. Subscribe ל-Win32_ProcessStartTrace דורש הרשאות Administrator.
- נמנעים מ-SELECT * ומ-polling במרווח קצר — מסננים ב-
-Filter/-Property/-KeyOnly. זוכרים ששאילתה מ-process 32bit נענית על ידי provider 32bit, שתאריכי DMTF צריכים את API ההמרה, וש-repository פגום מטופל בסדר verify → salvage, לא במחיקה. - לא מכניסים WMI לתחום שכבר יש לו מנגנון ייעודי (הגדרות, performance counters, קריאות API חד-פעמיות) — שומרים את WMI/CIM לשאילתות רוחביות ולשאילתות remote. המשפט הזה מסכם את מקומו.
מאמרים קשורים
- איך להריץ PowerShell מ-C# (CSharp) ולקבל את התוצאות כאובייקטים
- מתכוני פקודות PowerShell מעשיים — להרחיב את הכלים הקטנים שבשימוש יום-יומי
- ההבדלים בין Windows PowerShell 5.1 ל-PowerShell 7 — מדריך מעשי למיגרציית סקריפטים פנימיים
- שיטות עבודה מומלצות לבדיקה ולהצגה של מצב התקן חיצוני - תכנון שלא מסתפק ב”מחובר”
- קריאה בטוחה ל-Win32 API מ-C# — מדריך מעשי ל-P/Invoke (DllImport / LibraryImport / CsWin32)
- מהו TPM ב-Windows? — מדריך מאויר ל”כספת שלא מוציאה מפתחות” ול-Measured Boot
תחומי ייעוץ קשורים
KomuraSoft LLC מטפלת בבניית שליפת מידע חומרה, ניטור process ושאילתות מחשב remote עם WMI/CIM לתוך אפליקציות עסקיות, במיגרציית סקריפטים פנימיים מבוססי Get-WmiObject ל-CIM cmdlets, ובחקירת הבעיה מהסוג “עובד במכונת הפיתוח ונכשל בשגיאת הרשאות אצל הלקוח”. אפשר ללוות את כל הנתיב מניסוי ב-PowerShell עד מימוש מלא ב-C#.
מקורות
-
Microsoft Learn, About WMI. על כך ש-WMI הוא המימוש של Microsoft ל-WBEM (יוזמה תעשייתית לפיתוח טכנולוגיות תקן לגישה למידע ניהול בסביבה ארגונית), שמייצג יעדי ניהול באמצעות תקן התעשייה CIM (Common Information Model) שפותח ומתוחזק על ידי DMTF (Distributed Management Task Force); על כך ש-MI (Windows Management Infrastructure) מהדור הבא תואם לחלוטין ל-WMI המסורתי; ועל כך שחיבורי WMI remote משתמשים ב-DCOM, עם WinRM מבוסס WS-Management כחלופה. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Differences between Windows PowerShell 5.1 and PowerShell 7.x. על כך ש-WMI v1 cmdlets (Register-WmiEvent / Set-WmiInstance / Invoke-WmiMethod / Get-WmiObject / Remove-WmiObject) הוסרו מ-PowerShell, ועל כך ש-cmdlets של מודול CimCmdlets (WMI v2) מספקים אותה פונקציונליות עם תכונות חדשות ותחביר שעוצב מחדש. ↩ ↩2
-
Microsoft Learn, Get-CimInstance (CimCmdlets). על כך שכשלא מציינים ComputerName ולא CimSession מתחברים ל-WMI המקומי ב-session COM, ושציון -ComputerName יוצר session זמני בפרוטוקול WsMan; על כך שחיבור דרך CIM session מומלץ לביצועים כשמבצעים כמה פעולות מול אותו מחשב; על כך ש-
-Filterהוא פסוקית where של WQL/CQL בלי מילת המפתח WHERE; על כך ש--Propertyו--KeyOnlyמקטינים גודל אובייקט ותעבורת רשת; על כך שה-namespace שברירת המחדל הוא 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). על כך של-options של CIM session יש שתי קבוצות פרמטרים, ל-WsMan ול-DCOM; על כך ש-
-Protocolמקבל Dcom / Default / Wsman; על דוגמה להעברת option שנוצרה ב-New-CimSessionOption -Protocol Dcom אל -SessionOption של New-CimSession כדי ליצור CIM session ב-DCOM; ועל כך שרמת ההתחזות שברירת המחדל ל-session DCOM היא Impersonate. ↩ ↩2 -
Microsoft Learn, ManagementObjectSearcher Class (System.Management). על כך שזו class הכניסה הנפוצה ביותר לשליפת מידע ניהול, ששולפת אוסף אובייקטי ניהול על בסיס שאילתת WQL שצוינה; על כך שהיא מקבלת ObjectQuery ו-ManagementScope (WMI namespace) ומחזירה 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; על יצירת session דרך Create(computerName); על הרצת שאילתה דרך QueryInstances(namespace, queryDialect, query); ועל כך שהוא מספק EnumerateInstances / GetInstance / InvokeMethod / Subscribe יחד עם גרסאות async (*Async) של כל אחת, תוך מימוש IDisposable. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Register-CimIndicationEvent (CimCmdlets). על Subscribe ל-indication (event) לפי שם class או ביטוי שאילתה ומתן שם ל-subscription ב-
-SourceIdentifier; על דוגמת Subscribe ל-Win32_ProcessStartTrace, עם הערה שנדרשת הרצת PowerShell כ-Administrator; על דוגמה להפניה ל-ProcessName / ProcessId מתוך $Event.SourceEventArgs.NewEvent בבלוק הסקריפט של -Action; על חיבור ב-session זמני של WsMan כשמציינים -ComputerName, ומקומית ב-COM כשלא; ועל שימוש ב-Unregister-Event להסרת subscription. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, Win32_ProcessStartTrace class. על כך שזו event class שמציינת התחלת process חדש, עם מאפיינים כולל ProcessName / ProcessID / ParentProcessID / SessionID / Sid; על כך שמאפיין SECURITY_DESCRIPTOR הוא ה-descriptor ש-event provider משתמש בו כדי לקבוע אילו משתמשים יכולים לקבל את ה-event; ועל כך שה-namespace הוא Root\CIMV2, ומסופק על ידי kernel trace provider (Krnlprov.dll). ↩ ↩2 ↩3
-
Microsoft Learn, Win32_LogicalDisk class. על כך ש-Win32_LogicalDisk היא class הנגזרת מ-CIM_LogicalDisk שמייצגת התקן אחסון מקומי; על ערכי DriveType (2 = removable, 3 = דיסק מקומי, 4 = כונן רשת, 5 = CD, וכן הלאה); על כך ש-FreeSpace / Size הם ערכי בתים מסוג uint64; על כך ש-DeviceID הוא ה-key; ועל דוגמאות שאילתת VBScript / C# שמסננות ב-DriveType = 3. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Installation and configuration for Windows Remote Management. על כך ש-WinRM listener אינו מוגדר כברירת מחדל, כלומר אי אפשר לשלוח או לקבל הודעות WS-Management; על כך ש-winrm quickconfig מגדיר את השירות להפעלה אוטומטית, מגדיר HTTP/HTTPS listener ורושם חריג firewall; על כך שפורטים ברירת המחדל של WinRM 2.0 הם HTTP 5985 / HTTPS 5986; על הגדרת TrustedHosts בצמצום מרבי כשאי אפשר לכונן mutual authentication (Kerberos), למשל ב-workgroup; ועל security descriptor שברירת המחדל (RootSDDL) ששולט בגישה remote ל-listener, ועוד התצורה הנוספת שנדרשת כדי לאפשר למשתמשים שאינם Administrators להשתמש ב-WMI plugins. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, System.Management Namespace. על כך שזה ה-namespace ששואל את תשתית WMI דרך משפחת ה-classes ManagementObjectSearcher, ומטפל ב-event subscription דרך ManagementEventWatcher; על כך ש-WqlEventQuery מייצג שאילתת event בצורת WQL; ועל כך ש-ManagementDateTimeConverter מספק מתודות המרה בין ייצוגי תאריך/שעה ומרווח זמן של DMTF ל-DateTime / TimeSpan של CLR. ↩ ↩2 ↩3
-
Microsoft Learn, Requesting WMI Data on a 64-bit Platform. על כך שכש-provider קיים בגרסת 32bit ובגרסת 64bit, כברירת מחדל provider 32bit עונה לאפליקציות 32bit (כולל סקריפטים) ו-provider 64bit עונה לאפליקציות 64bit; על היכולת לבקש או לכפות את גרסת ה-provider שאינה ברירת המחדל דרך __ProviderArchitecture (32 או 64) ו-__RequiredArchitecture בהקשר (עם WBEM_E_PROVIDER_LOAD_FAILURE אם כופים לגרסה שאינה קיימת); ועל דוגמת Registry provider שבה לקוח 32bit מקבל נתונים מצד HKLM\SOFTWARE\Wow6432Node. ↩ ↩2 ↩3
-
Microsoft Learn, winmgmt. על כך ש-/verifyrepository של winmgmt.exe מבצע בדיקת עקביות של WMI repository; על כך ש-/salvagerepository מבצע בדיקת עקביות, ואם מתגלה חוסר עקביות בונה מחדש את ה-repository תוך מיזוג כל תוכן שהיה אפשר לקרוא; על כך ש-/resetrepository מחזיר את ה-repository למצבו בהתקנת מערכת ראשונית; על כך שה-repository מתפקד כמסד נתונים המורכב מהקבצים בתיקיית Repository; ועל כך ששגיאות שעולות דרך WMI לפעמים נובעות ממקום אחר במערכת, כך שיש להימנע ממחיקת ה-repository כתגובה ראשונה כי היא עלולה לגרום לנזק למערכת או לאפליקציות מותקנות. ↩ ↩2 ↩3
מאמרים קשורים
מאמרים עדכניים עם אותן תגיות, להעמקה בנושאים קרובים.
למה arguments נשברים — כללי command-line arguments ב-Windows
Windows מעביר ל-CreateProcess מחרוזת אחת שהמקבל מפצל. מכסה את כללי CommandLineToArgvW, CRT ו-.NET, ArgumentList, ובניה ב-C++.
הרשת עובדת אבל Windows מציג "אין אינטרנט" — לבודד NCSI, DNS, Proxy ו-VPN ב-Windows
למה Windows מציג "אין אינטרנט" בזמן שהרשת עובדת — נקודת המוצא היא קביעת הקישוריות של NCSI. כאן מפרידים בין DNS, proxy, VPN ו-captive port...
מה Fast Startup באמת עושה — למה "Shut down" ב-Windows אינו אותו דבר כמו Restart
Shutdown ב-Windows הוא hybrid shutdown כברירת מחדל, ושומר את ה-kernel ואת ה-drivers ל-hiberfil.sys. למה רק restart מאפס אותם, ומתי לכבות ...
Time Travel Debugging — להקליט ולהריץ אחורה באגים שלא משתחררים באפליקציות ארוכות-ריצה
באג פעם בחודש משאיר ב-crash dump רק את התוצאה. מקליטים ומריצים אחורה את הביצוע עם WinDbg Time Travel Debugging (TTD): TTD.exe, ring buffe...
סיום התמיכה ב-printer drivers של Windows — איך אפליקציות עסקיות מתכוננות להדפסת דוחות ותוויות
Microsoft מפסיקה בהדרגה printer drivers מסוג v3/v4. מה Windows protected print mode מסיר, ואיך עושים inventory ומכינים הדפסת דוחות ותוויו...
נושאים קשורים
העמודים האלה ממקמים את הנושא בהקשר רחב יותר של שירותים והחלטות.
נושאים טכניים ב-Windows
שער לנושאי פיתוח Windows, חקירת תקלות וניצול נכסים קיימים.
שירותים הקשורים לנושא הזה
המאמר קשור ישירות לשירותים הבאים.
פיתוח יישומי Windows
יישומים עסקיים, חיבור התקנים וכלי תקשורת, מהגדרת הדרישות ועד הפיתוח.
שאלות נפוצות
שאלות נפוצות בפניות בנושא המאמר.
- מה ההבדל בין WMI ל-CIM?
- CIM הוא מודל התקן התעשייתי לייצוג יעדי ניהול כמו מערכות והתקנים, ש-DMTF (Distributed Management Task Force) מגדיר ומתחזק. WMI הוא המימוש של Microsoft ל-WBEM — יוזמה שמשתמשת בתקן הזה — והוא מובנה ב-Windows. כלומר CIM הוא המפרט, ו-WMI הוא המימוש ב-Windows. Get-CimInstance ב-PowerShell ו-Microsoft.Management.Infrastructure ב-C# נקראים CIM כי הם API שעוקבים אחרי התקן, אבל הם מתחברים לאותה תשתית WMI. בפיתוח יום-יומי מספיק להבין ששואלים classes של WMI (Win32_* וכדומה) דרך API ממשפחת CIM.
- אפשר עדיין להשתמש ב-Get-WmiObject?
- ב-Windows PowerShell 5.1 הוא עדיין רץ, אבל מ-PowerShell 6 ואילך (כולל PowerShell 7 הנוכחי) cmdlets של WMI v1 — Get-WmiObject, Invoke-WmiMethod, Register-WmiEvent, Set-WmiInstance ו-Remove-WmiObject — הוסרו ואי אפשר להריץ אותם. אותה פונקציונליות מגיעה ממודול CimCmdlets (Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent וכדומה). בסקריפטים חדשים בטוח יותר לכתוב ב-CIM cmdlets גם אם הם ירוצו תחת 5.1. כך אין צורך לשכתב את חלק ה-WMI כשמגרים אחר כך ל-PowerShell 7.
- כדי לגשת ל-WMI מ-C#, להשתמש ב-System.Management או ב-Microsoft.Management.Infrastructure?
- שניהם ייעודיים ל-Windows, ומה-.NET הנוכחי מביאים אותם כחבילות NuGet. System.Management הוא ה-API הקלאסי: מעבירים WQL ל-ManagementObjectSearcher, והוא מספיק אם עיקר העבודה היא שליפת מידע מקומי. גם ManagementDateTimeConverter להמרת תאריכי DMTF נמצא שם. Microsoft.Management.Infrastructure (MI API), לעומת זאת, חולק את אותה מערכת טיפוסים (CimSession / CimInstance) עם CIM cmdlets של PowerShell, ומטפל באופן עקבי בשאילתות remote דרך WSMan, בגרסאות async של מתודות וב-Subscribe לאירועים. אם בונים שאילתות וניטור של מחשבים remote למוצר של ממש, הבחירה ב-MI API היא ההחלטה הסבירה.
- Get-CimInstance לא מתחבר למחשב remote. מה לבדוק?
- קודם בודקים אם WinRM מוגדר במכונת היעד. פעולת CIM עם -ComputerName יוצרת session זמני בפרוטוקול WSMan (WinRM), ולכן מניחה ששירות WinRM ו-listener רצים ביעד. winrm quickconfig מבצע את תצורת ברירת המחדל (הפעלת השירות, יצירת listener וחריג firewall). פורטים ברירת המחדל הם 5985 ל-HTTP ו-5986 ל-HTTPS, לכן בודקים גם firewall בדרך. בסביבת workgroup אין mutual authentication ב-Kerberos, וייתכן שצריך לרשום את היעד ב-TrustedHosts של הלקוח. ליעד שאי אפשר בכלל להגדיר בו WinRM אפשר במקום זאת להתחבר ב-DCOM עם option שנוצרה ב-New-CimSessionOption -Protocol Dcom.
- למה תאריך WMI חוזר בפורמט כמו 20260801100000.000000+540?
- תאריכי WMI מאוחסנים במחרוזת שמוגדרת במפרט CIM של DMTF (yyyymmddHHMMSS.mmmmmm±UUU, והסיומת היא offset מ-UTC בדקות). אם קוראים את הערך הגולמי עם Get-WmiObject הישן או עם System.Management, מקבלים את המחרוזת כמו שהיא. ב-C# (System.Management) מספק ManagementDateTimeConverter מתודות המרה בין פורמט DMTF ל-DateTime / TimeSpan, לכן משתמשים בהן במקום לחתוך את המחרוזת ביד. כששולפים דרך API ממשפחת CIM כמו Get-CimInstance, מאפייני תאריך כבר חוזרים מומרים ל-DateTime, ולכן הבעיה הזאת כלל לא מתעוררת.