C# - Windows Management Instrumentation (WMI) programozása C#-ból

forráskód letöltése
A Windows Management Instrumentation célja, hogy egy vállalati hálózati környezetben az információkezelést egységes technológiákkal oldja meg. A felhasználóknak lehetőségük van az alkalmazások, hálózatok vagy egyéb vállalati komponensek által szolgáltatott adatok lekérdezésére és módosítására. A fejlesztők számára rendelkezésre állnak programozási interfészek a C#, C++, Visual Basic, VBScript és HTML megvalósítások kidolgozására. Emellett bármilyen szoftverfejlesztői környezetben képesek vagyunk WMI-t programozni, mely támogatja a COM fejlesztést, így például Delphi-t is használhatunk e célra. A WMI szorosan képes együttműködni a Windows összetevők széles skálájával, köztük az Active Directory-val is. Felhasználható gyakorlatilag az összes 32 bites (és már a 64 bites) Windows operációs rendszerben: Windows 95/98/Me/NT/2000/XP és a .NET szerverekben is. Közvetlen beépített támogatást a .NET és a 2000/XP verzióik biztosítanak. Gyakorlati oldalról nézve a WMI-n keresztül lekérdezhetjük az adott számítógép minden hardver összetevőjének a paramétereit kezdve a hálózati kártya MAC address-étől a memória méreten át a háttértárak információjáig. Persze a WMI nem csak a hardverrel kapcsolatos tennivalókat látja el, hanem szinte mindent: operációs rendszer, telepített alkalmazások, teljesítmény számlálók, stb.
Néhány alkalmazási terület
Megvalósítható vele a Windows konfigurációs feladatainak elvégzése, felhasználói fiókok kezelése, jogosultságok beállítása, az objektumok biztonsági mentése. Meghajtók kezelése a helyi és a hálózat távoli gépén egyaránt: lekérdezhetők és módosíthatók az eszközök tulajdonságai, beállításai. Olyan rendszerszintű műveletek elvégzésére is alkalmas, mint a lapozófájl méretének megváltoztatása vagy a helyi vagy távoli gépek leállítása, újraindítása.
Windows 2000-ben a felügyeleti eszközök Kezelés (Manage) konzolja a benne lévő segédprogramokkal és a Rendszertulajdonságok (System Properties) szolgáltatás aktívan használja.
A CIM modell
A WMI integrált támogatást biztosít a CIM modellhez (Common Information Model = Általános információs modell). A CIM írja le egy vállalati környezetben található objektumokat, tulajdonképpen egy hatalmas adatbázis. Ha pontosabban akarunk fogalmazni, akkor inkább a különböző vállalati részekről információt tároló, programozási nyelv független, kiterjeszthető, objektum orientált adatmodell megnevezést kell használnunk. A fejlesztő a WMI szolgáltatáson keresztül elérve a CIM-t kezelheti a merevlemez meghajtókat, alkalmazásokat, hálózati elemeket (pl. router) vagy hálózatba kapcsolt egyedi, programozható eszközöket (akár a légkondicionálót is).
A WMI osztályok (WMI Classes)
  • System Classes: A CIM előre definiált osztályainak egy gyűjteménye. Olyan elemeket tartalmaz, mint események regisztrálása, rendszer biztonsági beállítások, figyelmeztető üzenetek generálása. Az osztályok egyedi elnevezési rendszerrel rendelkeznek két aláhúzott vonalat követ az osztály neve (pl: __NotifyStatus, __Parameters, __SystemSecurity).
  • Win32 Classes: A számítógép hardver elemeinek elérését biztosítja, még a processzor hűtőventillátor kezeléséhez (fordulatszám lekérdezés, szabályozás) is tartalmaz osztályokat. A teljes Windows operációs rendszert alkotó objektumok elérhetők (pl.: szolgáltatások, hálózati megosztások, felhasználói fiókok, ütemezett feladatok, Registry, termék aktiválás, COM, telepített alkalmazások, teljesítmény számlálók, stb.).
  • Standard Consumer Classes: Az egyéb kategóriába sorolható, bár cseppet sem mellékes: szkriptek által generált események kezelése, regisztrálása, naplózása, SMTP szolgáltatás és a parancssor által generált események kezelése.
Ezek csak a főbb osztályok, de a programozási nyelvek támogatásához, csoportházirendek kezeléséhez és esemény összefüggések vizsgálatához is találunk több száz függvényt, metódust és tulajdonságot. Ezenkívül, úgynevezett Provider-eken keresztül hozzáférhető az Active Directory, DFS, lemezkvóta, energiaellátás, SNMP és sok más része is a számítógépnek hardveres és szoftveres szemszögből egyaránt.
Hogy lehet ezt a rengeteg adatot elérni?
Mint említettük a WMI rendelkezik programozási interfészekkel, így gyakorlatilag bármilyen alkalmazás fejlesztői környezetből használható. A motort egy WQL (WMI Query Language) elnevezésű lekérdezőnyelv adja, ahol szabályos SQL parancsokkal dolgozhatunk.
Névterek (Namespaces)
Az osztályok logikailag csoportokba vannak rendezve a könnyebb kezelhetőség kedvéért, ezeket a csoportokat névtereknek nevezzük. Ábrázolását egy faszerkezetben kell elképzelni, ahol a gyökérből (root) indulunk ki és az ágak különböző mélységig egymásba ágyazódnak (pl.: root\cimv2 vagy root\directory\LDAP).
Biztonság
A WMI hitelesítési eljárások használatával fér hozzá az objektumokhoz, ezért a futtatáshoz megfelelő jogokkal kell rendelkezni. Képes a felhasználó nevét és jelszavát használni. Ha az adott személy jogosult az erőforrás elérésére sikeres lesz a hozzáférés.
Nézzünk egy egyszerű WMI-t használó VBScript-et, amellyel kiléphetünk az operációs rendszerből és újraindíthatjuk a számítógépet:
Set OpSysSet = GetObject("winmgmts:{(Shutdown)}//./root/cimv2").ExecQuery("select * from Win32_OperatingSystem where Primary=true")
for each OpSys in OpSysSet
  OpSys.Reboot()
next
A "winmgmts" szolgáltatás "Win32_OperatingSystem" osztály elérésével meghívtuk a gép újraindításáért felelős metódusát (Reboot). Jól látszik a WQL lekérdezés használata is. A Windows 9x rendszerekben a Windows Management szolgáltatás egy WINMGMTS.EXE programon keresztül futott. Windows NT/2000/XP-ben ez bekerült az SVCHOST szolgáltatásba.
Térjünk most át arra, hogy miként használhatjuk mindezt egy C# alkalmazásból.
Szükségünk lesz a System.Managment névtérre. Ennek eléréséhez válasszuk a Project - Add referece menüpontot, majd a .NET lapon keressük elő a System.Managment tételt. Select, majd OK gomb után már hivatkozhatunk is rá a forráskódban.
Készítsünk elsőnek egy olyan lekérdezést, melyből megtudhatjuk, hogy milyen környezeti változók vannak és azoknak mi az értékük.
Egy WMI lekérdezést a SelectQuery osztály segítségével hozhatunk létre. Ennek rögtön a konstruktorában adhatjuk meg, hogy mi is legyen a lekérdezés.
    private void button1_Click(object sender, System.EventArgs e)
    {
      SelectQuery sq = new SelectQuery("Win32_Environment", "UserName=\"<SYSTEM>\"");
Ezt követően létrehozunk egy ManagementObjectSearcher osztályt, mely abban segít bennünket, hogy az összes környezeti változón, mint egy tömbön, végig tudjunk menni.
      ManagementObjectSearcher mos= new ManagementObjectSearcher(sq);
      listBox1.Items.Clear();
Ezt a tömböt szolgáltatja az osztály Get függvénye.
      foreach (ManagementBaseObject mbo in mos.Get()) 
      {
A tömb minden eleme ManagementBaseObject osztály típusú. Ennek Items property-jén keresztül elérhetjük az egyes paramétereket, azok neveivel hivatkozva rájuk. Így a környezeti változó nevét a Name, míg értékét a VariableValue tulajdonságból tudhatjuk meg.
        listBox1.Items.Add(mbo["Name"] + ": " + mbo["VariableValue"]);
      }
    }
Feladatunk második részében készítsünk egy olyan lekérdezést, mely átfogó képet ad a rendszerben lévő meghajtókról. A lekérdezést úgy valósítjuk meg, hogy egy ciklussal az objektum összes property-jének nevét és értékét kiírjuk, így nem is kell ismernünk, hogy az adott objektum milyen tulajdonságokkal rendelkezik.
Ismét a SelectQuery osztály segítségével adunk meg egy WQL lekérdezést, mely az összes logikai meghajtóra vonatkozik.
    private void button2_Click(object sender, System.EventArgs e)
    {
      SelectQuery sq = new SelectQuery("SELECT * FROM Win32_LogicalDisk");
Ismét egy ciklust kezdünk, mint az előbb, mely végigmegy az összes talált elemen, vagyis az összes meghajtón.
Létrehozunk egy tömböt, melynek minden eleme PropertyData osztály típus. Ebbe tároljuk majd el az objektum tulajdonságainak adatait.
      foreach (ManagementBaseObject mbo in mos.Get()) 
      {
        pd = new PropertyData[mbo.Properties.Count];
Ehhez a CopyTo függvényt használhatjuk. Ahol első paraméterként a létrehozott tömböt, míg másodikként a kezdő indexet adhatjuk meg, ahonnan a másolást kezdeni kell.
        mbo.Properties.CopyTo(pd, 0);
Most már csak egy for ciklus kell, mely végigmegy a tömb minden elemén.
        for (int i = 0; i < mbo.Properties.Count-1; i++)
Itt lekérdezzük az adott property értékét és tároljuk egy sztringbe.
            s = pd[i].Value.ToString();
Majd hozzáadjuk a ListBox-hoz a property nevével együtt annak értékét is megjelenítve.
          listBox1.Items.Add(pd[i].Name + ": " + s);
        }
      }   
    }
A ciklus végére egy teljes listát kapunk az összes logikai meghajtóról és azok minden elérhető információjáról.