Bejelentkezés

E-mail: 
Jelszó: 
| Regisztráció | Jelszó? |
 



Menü

.Net világ havilap 2004. szeptember - 5. oldal

Lássuk, miként működik: GAC, lépésről-lépésre

Akinek  nem  az  elmélet  fontos,  aki  ssnem  csak  "hallani"  akar  róla,  aki  a  gyakorlatban  használni  szeretné,  aki  valós  élethelyzetekre  keresi  a  megoldást,  az  jó  helyen  jár.  E  cikkünkben  lépésről-lépésre  bemutatjuk  miként  alkalmazható  a  Global  Assembly  Cache  (GAC)  a  gyakorlatban.  Megkeressük  előnyeit,  melyeknek  csendben  örülünk  és  felkutatjuk  buktatóit,  melyekre  azonnali  megoldást  keresünk. 

A  mellékelt  példaprogram  megnyitása  előtt  szükséges  egy  GACWeb  nevű  virtuális  mappa  létrehozása  az  IIS-ben.  Ennek  legegyszerűbb  módja,  ha  az  Intézőben  az  adott  mappán  jobb  gombbal  kattintunk  és  a  Tulajdonság  menüpontot  választjuk,  majd  a  megjelenő  ablak  Webmegosztás  lapján  engedélyezzük  a  mappa  megosztását.Global  Assembly  Cache  (GAC)A  múltHajdanán,  még  az  őskorban  éltek  elvetemült  DLL-ek,  melyek  vették  a  bátorságot  és  több,  különféle  szoftverrel  megérkezvén  gépünkre,  ugyanazon  a  néven  egymást  felül  írogatták  különböző  verziószámok  alatt.  Így  amíg  az  egyik  program  egy  újabb  DLL-t  akart  használni,  addig  egy  másiknak  elég  lett  volna  a  régi.  Sötét  idők  jöttek  el:  a  bonyodalmak  kusza  világa  köszöntött  gépünkre,  minél  több  szoftvert  telepítettünk  fel  arra.  A  DLL-ek  verzióbeli  különbsége  sok  esetben  okozott  program,  súlyosabb  esetben  akár  rendszerösszeomlást  is.  Jobb  híján  így  minden  EXE  rövidpórázon,  maga  mellett  tartotta  DLL-jeit,  hogy  ne  kószáljanak  a  merevlemezen,  mert  jártukban-keltükben  csak  gondot  okoznak.  Ez  persze  csak  növelte  a  káoszt,  hiszen  így  egyazon  DLL  számos  verziója  számos  mappában  fellelhető  volt.  A  szoftverek  frissítése,  karbantartása  már-már  ellehetetlenült.  Meg  is  született  hát  a  találó  elnevezése  e  problémának,  mely  "DLL  Hell"  (DLL  pokol)  néven  került  a  köztudatba.  Ideje  volt  hát  újra  gondolni  a  DLL-ek  körüli  bonyodalmakat,  hogy  arra  megoldás  szülessen  és  egy  szép  napon  felragyogott  a  GAC  világa.  A  jelenA  GAC-ot  tekinthetjük  egy  olyan  speciális  mappának,  mely  képes  azonos  nevű,  de  különböző  verziószámú  DLL-eket  tárolni  egy  helyen.  Így  már  nem  történhet  meg,  hogy  egy  DLL  felülír  egy  másikat,  csak  azért  mert  ő  újabb,  mint  elődje.  Mivel  a  GAC  egy  helyen,  egy  egységben  tárol  mindent,  így  az  sem  probléma  tovább,  hogy  szétszórtan  hevernek  DLL-jeink,  amerre  csak  akarnak.  Az  egy  helyen  történő  tárolás  előnye,  hogy  alkalmazásunk  bárhol  is  legyen,  a  DLL-ek  elérhetők  a  programunk  számára.Már-már  azt  hihetnénk,  hogy  itt  a  Kánaán,  erre  mi  történik?  A  GAC  szép  csendben  gyűjti  a  DLL-ek  sorát  és  egy-egy  DLL-ből  már  lehet,  hogy  10-20,  vagy  akár  még  több  verziót  is  tárol.  Így  aztán  kezdődhet  a  káosz  ismét.  Egy  idő  után  már  nehéz  lesz  kibogozni,  hogy  melyik  verziójú  DLL  kinek  és  egyáltalán  minek  is  kell,  ha  kell.  Mert  lehet,  hogy  már  nincs  is  olyan  alkalmazás  a  gépen,  amelyik  az  adott  verziójú  DLL-t  használná.  Na  de  merjük-e  törölni  a  GAC-ból  anélkül,  hogy  biztosak  lehetnénk  afelől,  hogy  egyik  felhasználónknál  sincs  olyan  program,  mely  pont  erre  a  DLL-re  hivatkozik.  Ki  az  a  rendszergazda,  aki  tengernyi  DLL  ezernyi  verzióját  folyamatosan  jegyzi,  figyeli,  karbantartja  minden  általa  felügyelt  számítógépen,  éveken  át.  Arról  nem  is  beszélve,  hogy  ha  egy  DLL-ből  újabb  verziót  teszünk  a  GAC-ba,  akkor  a  régi  programjaink  még  mindig  a  régi  DLL-t  használják.  Automatikusan  nem  keresi  majd  senki  az  új  DLL-t,  így  hiába  is  frissítettük  azt  és  tettük  a  közös  gyűjtőhelyre,  programjainkkal  még  külön  tudatni  kellene,  hogy  felejtsék  már  el  a  régit,  ha  szükségük  van  az  újra.Így  aztán  a  mese  végén  ismét  eljutunk  egy  újabb  pokolba,  csak  most  más  van  kiírva  a  névtáblára.  Az  újabb  pokol  már  "Version  Hell"  néven  mutatkozott  be.A  jövőHogy  mit  hoz  a  jövő,  az  nagy  kérdés.  Megoldást?  Vagy  egy  újabb  problémát?  Ma  még  nehéz  jósolni,  hiszen  a  GAC-ra  is  megoldásként  tekinthettünk  annak  idején  és  való  igaz,  hogy  a  problémákat  maradéktalanul  meg  is  oldja,  sokkal  jobb  helyzetet  teremt  mint  volt,  de  az  új  megoldás  egyúttal  új  problémákat  is  felvet.  A  megoldás  persze  már  készül.  A  Windows  "Longhorn"  verziója,  illetve  az  új  Visual  Studio.NET  "Orcas"  verziójának  megjelenésekor  debütál  a  .NET  3.0-ás  verziója  is.  Ebben  a  számos  újdonság  mellett  a  DLL-ek  kezelésére  is  egy  új  módszer  mutatkozik  be,  mely  felszámolja  a  Version  Hell  problémáit.  A  gondok  kivédésére  két  új  kategóriát  vezetnek  be:  Library  és  Platform  DLL.Ez  az  osztályozás  határozza  meg,  hogy  a  program  melyik  DLL-t  töltse  be.  A  platform  DLL-ben  azokat  az  interfészeket,  absztrakt  osztályokat,  alapvető  típusokat  adjuk  meg,  melyek  biztosítják  a  funkcióink  elérését,  akár  a  visszafelé  kompatibilitás  szükségessége  révén  is.  A  platform  DLL-nek  három  további  kategóriája  létezik:  system-wide,  process-wide  és  domain-wide.  Ezen  alkategóriák  meghatározzák  a  DLL  hatókörét,  mivel  előírhatjuk,  hogy  a  DLL  például  rendszerszinten  minden  alkalmazás  számára  alapvető  legyen,  vagy  megadhatjuk,  hogy  csak  egy  alkalmazás  számára  legyen  az.A  .NET  Framework  3.0  verzióban  ránk  köszönt  tehát  egy  új  korszak,  mely  eltörli  a  "Version  Hell"-t  és  megoldást  kínál  az  így  felmerült  problémákra.  De  azért  legyünk  résen:  az  ördög  nem  alszik,  lehet,  hogy  már  fűti  a  legújabb  poklot...Lépésről-lépésreTekintsük  most  át,  miként  is  tudjuk  igénybe  venni  a  GAC  szolgáltatásait  és  hogyan  kerülhetjük  el  buktatóit.  Ha  ésszerűen  járunk  el  minden  lépésnél,  akkor  látjuk  majd,  hogy  nem  is  olyan  pokoli  a  Version  Hell,  mint  amennyire  az  látszik  elsőre.Készítsünk  egy  új  Windows-os  alkalmazást,  melyhez  rögtön  tegyünk  hozzá  egy  DLL-t  is,  GACTestLibrary  néven:  DLL  létrehozásaA  DLL  létrehozásához  egy  Class  Library  típusú  projektet  kell  a  Solution-hoz  adnunk.  A  DLL  tartalmazni  fog  egy  Class1  nevű  osztályt,  melynek  egyetlen  statikus  függvénye  lesz  GetDateTime  névvel.  E  függvény  nem  tesz  semmi  egyebet,  mint  sztringként  visszaadja  az  aktuális  időpontot.    public  class  Class1    {        public  static  string  GetDateTime()        {            return  DateTime.Now.ToString();        }    }Most  fordítsuk  le  a  projektet,  hogy  létrejöjjön  a  DLL  állomány.  Kis  időre  feledjük  el  a  GAC  létezését  és  használjuk  fel  e  DLL-t  a  hagyományos  módon.  A  cél  nyilvánvalóan  az  lesz,  hogy  az  EXE-ből  meghívhassuk  a  DLL-ben  lévő  GetDateTime  függvényt.  Referencia  hozzáadásaEhhez  egy  referenciát  kell  a  Windows-os  alkalmazásunkhoz  adni.  Válasszuk  a  Project  -  Add  reference  menüpontot,  majd  a  megjelenő  ablakból  a  Projects  lapon  a  GACTestLibrary-t  válasszuk  ki,  majd  Select  és  OK  gomb  és  már  használható  is  a  DLL  az  EXE-ből.  Hozzáadott  referenciaHa  mindent  jó  csináltunk  eddig,  akkor  a  fenti  képen  látható  módon  néz  ki  a  Solution  Explorer  tartalma.  Ebben  jól  látható  az  imént  hozzáadott  referencia  GACTestLibrary  néven.Tegyünk  most  a  Form1-re  egy  Label-t  és  egy  nyomógombot.  A  gombra  történő  kattintáskor  hívjuk  meg  a  DLL-ben  lévő  függvényt.  Ehhez  azonban  hivatkoznunk  is  kell  a  GACTestLibrary  névtérre:        using  GACTestLibrary;A  GetDateTime  függvény  hívása  igen  egyszerűen  megvalósítható  ezek  után:        private  void  button1_Click(object  sender,              System.EventArgs  e)        {            label1.Text  =  Class1.GetDateTime();        }  Programunk  aktuális  állapotaFuttassuk  a  programot  és  próbáljuk  ki  működését.  Látható,  hogy  megjelenik  a  Label-en  a  pontos  idő,  így  bizakodhatunk:  sikerrel  játunk.DLL-t  a  GAC-ba!Ahhoz,  hogy  a  DLL-t  bedobhassuk  a  GAC-ba,  csöppnyi  kiegészítő  műveletet  kell  végeznünk,  mely  nem  lesz  más,  mint  a  DLL  aláírása.DLL  aláírásaTegyünk  egy  kis  kitérőt:  tudjuk  ugye,  hogy  a  GAC  azonos  nevű  állományokat  képes  tárolni.  Általában  különbséget  az  azonos  nevű  DLL-ek  között  a  verziószámuk  jelent  csupán.  Viszont  ez  sem  biztosít  egyedi  azonosítási  lehetőséget  100%-os  mértékben,  amire  azonban  nagy  szükség  van.  Előfordulhatna  az  az  extrém  helyzet,  hogy  két  különböző  szoftverfejlesztő  cég  egy-egy  terméke  tartalmaz  azonos  nevű  és  azonos  verziószámú  DLL-t.  Ilyen  esetben,  ha  mindkét  szoftvert  feltelepítenénk  a  gépünkre,  akkor  nem  lehetne  megkülönböztetni  egymástól  a  DLL-eket,  pedig  a  programok  működéséhez  nem  mindegy,  hogy  mikor  melyik  DLL-t  veszik  elő  használatra.  Hogy  biztosak  lehessünk  abban,  hogy  minden  DLL  egyedileg  azonosítható,  ún.  erős  kulcssal  alá  kell  írni  azokat  a  GAC-ba  helyezésük  előtt.  E  művelet  csupán  megnevezésében  lehet  rémisztő  dolog,  amúgy  egy  egyszerű  eljárásról  van  szó.  Ennek  lényege,  hogy  a  DLL-hez  generáltatunk  egy  egyedi  kulcsot,  majd  ezzel  együtt  újra  fordítjuk  azt.  Így  a  DLL  tartalmaz  egy  olyan  globálisan  egyedi  azonosítót,  mely  alapján  nem  fordulhat  elő  DLL-ek  közötti  keveredés.A  kulcs  generálásához  kapunk  a  Visual  Studio.NET-tel  egy  SN.EXE  nevű  kis  parancssori  segéd  alkalmazást,  melynek  paraméterként  megadva  a  DLL  elérési  útját,  nevét,  máris  létrehoz  egy  kulcsot  hozzá  egy  SNK  kiterjesztésű  állományban.Az  ilyen  kis  parancssori  alkalmazásoknak  megvan  az  a  nagyszerű  varázsa,  hogy  könnyen  használhatók  programból  hívva,  vagy  akár  batch  file-t  is  készíthetünk  használatukhoz.  Roppant  nagy  hátrányuk  viszont  az,  hogy  ha  kézzel  próbáljuk  meg  használni,  akkor  elég  sokat  kell  a  parancssorba  gépelni,  ami  sok  elgépelési  lehetőséget  (gondoljunk  csak  a  hosszú  elérési  utakra)  és  egyéb  nehézséget  rejt.Mivel  szerencsére  már  nem  DOS-t  használunk,  így  a  parancssori  gépelgetés  helyett  egy  újabb  kis  kitérővel  egészítsük  ki  a  Windows  Intézőjének  gyorsmenüjét  úgy,  hogy  ha  DLL-en  kattintunk  jobb  gombbal,  akkor  legyen  egy  olyan  menüpont,  melyet  kiválasztva  az  adott  DLL-hez  legenerálódik  a  szükséges  SNK  állomány.  Ez  a  megoldás  már  sokkal  inkább  programozó  barát,  mint  az  SN.EXE  manuális  használata.Ennek  megoldásához  csupán  a  Windows  regisztrációs  adatbázisában  kell  egy  kicsit  matatni.  A  megoldás  abban  rejlik,  hogy  a  HKEY_CLASSES_ROOT  főkulcson  belüli  DllFile  bejegyzésnél  egy  új  kulcsot  kell  elhelyezni,  melynek  révén  a  DLL  kiterjesztésű  állományokhoz  létrejön  egy  új  menüpont.  E  menüvel  pedig  elindíthatunk  bármilyen  programot,  mely  történetesen  az  SN.EXE  lesz  és  amelynek  paraméterként  egyúttal  át  is  adhatjuk  az  éppen  kijelölt  DLL  elérési  útját  és  nevét,  így  az  képes  lesz  a  kulcs  generálására.A  Windows  regisztrációs  adatbázis  módosítását  elvégezhetjük  a  mellékelt  példaprogramban  található  AddSNKeyMenu.reg  állomány  lefuttatásával.  Az  AddSNKeyMenu.reg  futtatása  előtt  ellenőrizze,  hogy  a  benne  található  elérési  út  egyezik-e  az  Ön  rendszerével.  Ha  más  helyre  telepítette  a  Visual  Studio.NET-et,  akkor  módosítsa  az  itt  található  elérési  utat  úgy,  hogy  az  SN.EXE  elérhetővé  váljon.AddSNKeyMenu.regA  Windows  regisztrációs  adatbázishoz  tehát  hozzáadunk  egy  új  bejegyzést,  mely  egy  új  menüpontként  jelentkezik.  A  menüt  választva  futtatjuk  az  SN.EXE  alkalmazást,  melynél  paraméterként  (%1)  megadjuk  a  kiválasztott  DLL-t.  A  szükséges  kulcs  generálása  ekkor  megtörténik  és  létrejön  az  SNK  állomány.Windows  Registry  Editor  Version  5.00[HKEY_CLASSES_ROOT\dllfile\Shell\Generate]@="Aláírás  állomány  (.snk)  generálása"[HKEY_CLASSES_ROOT\dllfile\Shell\Generate\Command]@="\"c:\\Program  Files\\Microsoft  Visual  Studio  .NET\\FrameworkSDK\\Bin\\sn.exe\"  -k  \"%1\".snk"RemoveSNKeyMenu.regA  mellékelt  RemoveSNKeyMenu.reg  használatával  eltávolítható  a  Windows  regisztrációs  adatbázisból  az  AddSNKeyMenu.reg  által  hozzáadott  tartalom.Windows  Registry  Editor  Version  5.00[-HKEY_CLASSES_ROOT\dllfile\Shell\Generate]  Kulcs  generálás  intézővelMost  már  egyszerű  a  helyzet:  keressük  elő  a  GACTestLibrary.dll  állományt  a  Windows  Intézővel  és  kattintsunk  rajta  jobb  gombbal.  A  megjelenő  új  "Aláírás  állomány  (.snk)  generálása"  menüpontot  választva  elindíthatjuk  az  SN.EXE-t  és  így  legenerálhatjuk  az  adott  DLL-hez  az  egyedi  kulcsot.  A  létrejött  SNK  állományEkkor  létrejön  a  GACTestLibrary.dll.snk  állomány,  melybe  belenézni  nem  érdemes,  mert  tartalma  kódolt,  bináris.Kulcs  felhasználásaHa  adott  a  kulcs,  akkor  rátérhetünk  annak  felhasználására:  nyissuk  meg  a  Windows-os  alkalmazáshoz  tartozó  AssemblyInfo.cs  állományt  a  Visual  Studio.NET-ben.  Ebben  találunk  egy  AssemblyKeyFile  nevű  bejegyzést,  mely  jelenleg  egy  üres  sztringet  tartalmaz.  E  sztringbe  adjuk  meg  a  létrehozott  SNK  állomány  nevét.[assembly:        AssemblyKeyFile("GACTestLibrary.dll.snk")]Ezt  követően  fordítsuk  újra  a  DLL-t,  mely  ezek  után  máris  alkalmas  arra,  hogy  a  GAC-ba  helyezzük.Jelenleg  most  a  DLL  mellett  található  az  SNK  állomány.  Ezzel  nem  is  lesz  problémánk,  de  ha  egyszer  debug  verzió  helyett  release  verziót  fordítanánk,  akkor  az  egy  új  mappába  jön  létre,  ahol  már  nem  lesz  ott  az  SNK  állomány,  ami  azonban  nem  kívánt  problémákhoz  vezet.  Ezért  célszerű  áthelyezni  az  SNK  állományt  a  projekt  mappájába  a  bin\Debug-ból.  Ekkor  módosítsuk  az  AssemblyKeyFile-t  úgy,  hogy  relatív  elérési  utat  adunk  meg,  így  mindegy  lesz,  hogy  a  DLL  a  debug,  vagy  a  release  mappába  kerül.  [assembly:  AssemblyKeyFile("..\\..\\GACTestLibrary.dll.snk")]DLL  GAC-ba  helyezéseMost  már  semmi  sem  állhatja  utunkat,  hogy  a  DLL-t  végre  a  GAC-ba  tehessük.  Erre  több  módszer  is  létezik.  Egyik  a  GacUtil.EXE  parancssori  alkalmazás,  melynek  előnye  és  hátránya  pont  ugyanaz,  mint  az  SN.EXE  esetén.  Így  most  felejtsük  is  el  ezt  a  lehetőséget  és  keressük  elő  a  Vezérlőpult  -  Felügyeleti  eszközök  -  Microsoft  .NET  Framework  Configuration  alkalmazást.  E  kis  programocska  sok  minden  mellett  arra  is  jó,  hogy  kezeljük,  megtekintsük  a  GAC  tartalmát.    GAC  kezeléseA  My  Computer  -  Assembly  Cache  elem  alatt  érhetjük  el  a  GAC  lehetőségeit.  Ezen  az  elemen  jobb  egérgombbal  kattintva  kapunk  egy  Add  menüpontot,  melyet  választva  előkereshetjük  a  GAC-ba  helyezni  kívánt  DLL-t.  DLL  a  GAC-banEkkor  máris  bekerül  a  DLL-ünk  a  GAC-ba.  Látható  lesz  a  neve,  verzió  száma,  nyelve  és  publikus  kulcsának  azonosítója.Hogyan  továbbHa  véget  ért  a  DLL  sikeres  GAC-ba  helyezése  miatt  tartott  ünnepi  összejövetelünk,  akkor  térjünk  vissza  programunkhoz,  ugyanis  a  játszma  nagy  része  még  hátra  van.  Most  ugye  van  egy  DLL  a  bin/Debug  mappában  és  egy  GAC-ban,  na  meg  egy  másolat  az  EXE  mappájában  is!  Vajon  melyiket  használhatja  a  programunk  jelenleg???  Bizony  hiába  minden  erőfeszítés,  az  EXE-nk  még  most  is  a  helyi,  bin/Debug-ból  származó  másolat  DLL-t  használja.  Ez  azért  van  így,  mert  felvettük  referenciaként  a  projekthez  tartozó  DLL-t,  melyről  minden  fordításkor  egy  másolat  kerül  az  EXE  mellé,  mivel  ez  az  alapértelmezett  eset.  Ha  azt  szeretnénk  elérni,  hogy  a  GAC-ban  lévő  DLL-t  használja  programunk,  akkor  tegyük  a  következőket:  GACTestLibrary  ismételt  hozzáadásaA  GACTestLibrary  referenciát  távolítsuk  el  a  Windows-os  alkalmazás  referenciái  közül  és  adjuk  újra  hozzá,  de  most  már  a  .NET  lapon  lévő  Browse  gombra  kattintva.  A  megjelenő  ablakból  keressük  elő  a  helyi,  bin/Debug  mappában  lévő  GACTestLibrary.dll-t  és  jelöljük  ki.  Nyugalom,  nem  elírás,  ismét  a  bin/Debug  mappában  lévő  DLL-t  kell  előkeresni,  mivel  a  GAC-ból  közvetlenül  nem  válogathatunk,  ám  minden  referencia  hozzáadásakor  a  keretrendszer  ellenőrzi,  hogy  a  kiválasztott  DLL  megtalálható-e  a  GAC-ban  vagy  sem.  Ha  igen,  akkor  az  élvez  elsőbbséget.  EllenőrzésHa  a  referencia  hozzáadása  után  a  Solution  Explorer-ben  kijelöljük  a  GACTestLibrary-t  és  megnézzük  a  Properties  lapon  a  tulajdonságait  a  referenciának,  akkor  a  Copy  Local  property  hamis  lesz.  Ennek  következményeképpen  az  EXE  mellé  nem  kerül  lokális  másolat  a  DLL-ben,  így  az  mindig  a  GAC-ból  kerül  felhasználásra.  Programunk  kezdetén  a  DLL  ezért  került  mindig  átmásolásra  az  EXE  mellé,  mert  e  property  igaz  értéket  kapott.  Példaprogram  aktuális  állapotaTöröljük  manuálisan  az  EXE  mellett  lévő  DLL-t,  hiszen  arra  többé  nem  lesz  szükség  és  futtassuk  újra  a  programot.  Az  eredményből  látható  lesz,  hogy  a  DLL-ben  található  funkció  elérhető  még  mindig,  pedig  az  EXE  mellett  már  nincs  ott  a  DLL,  így  az  csak  a  GAC-ból  származó  lehet.Mi  történik,  ha  most  változtatunk  a  DLL  kódján?Az  élet  általában  nem  áll  meg  még  ezen  a  pontos  sem.  A  DLL  valószínűleg  még  hosszas  fejlesztés  előtt  áll,  így  azt  rendszeresen  újrafordítjuk  minden  módosítás  után.  Mi  is  történik  vajon  ilyen  esetben?  Tegyünk  egy  próbát  és  változtassunk  a  DLL  kódján:          public  static  string  GetDateTime()        {            return  "version  2:  "  +                  DateTime.Now.ToString();        }    Változtatás  után  futtassuk  a  programot.  Látható  lesz,  hogy  a  "version  2"  felirat  nem  lesz  látható,  vagyis  az  EXE  még  mindig  a  GAC-ban  lévő  régi  DLL-t  használja,  amiből  látszik,  hogy  a  GAC  nem  frissül  magától.  Mivel  csak  a  DLL  forrásán  változtattunk,  így  nyilvánvaló,  hogy  az  EXE  kódja  nem  változott,  a  GAC-ban  lévő  DLL  nem  változott,  csak  a  lokális  DLL  kódja  más.  Fordítsuk  most  újra  a  teljes  projektet!  Majd  futtassuk  így  az  EXE-t.  Hibaüzenet  verzióbeli  különbség  miattAmikor  használni  szeretnénk  a  programunk  nyomógombját,  egy  hibaüzenetet  kapunk  pontos  idő  helyett.  A  hibaüzenetből  kitűnik,  hogy  az  EXE  nem  talál  olyan  verziójú  DLL-t,  amelyre  szüksége  lenne.Megváltozott  a  verziószám!Amikor  a  DLL-t  megváltoztattuk  és  újrafordítottuk,  akkor  egyúttal  a  DLL  automatikus  verziószám  kezelése  miatt  új  verziószámot  kapott.  Amikor  az  EXE-t  is  újra  fordítottuk,  akkor  az  is  "megjegyezte"  hogy  mi  az  aktuális  verziószáma  a  DLL-nek,  melyre  szüksége  van,  de  a  futás  során  ilyen  verziószámú  DLL-t  már  nem  talált  sem  a  GAC-ban,  sem  maga  mellett  lokális  másolatként,  ezért  nem  tehetett  mást,  mint  a  fenti  hibaüzenettel  leállt.  Új  DLL  GAC-ba  helyezéseAdjuk  a  GAC-hoz  az  új  DLL-t  is.  Látható  lesz,  hogy  két  azonos  nevű,  de  különböző  verziójú  DLL  van  a  GAC-ban,  1.0.1687.34884  és  1.0.1687.34913  számon.Ha  most  futtatjuk  az  EXE-t,  akkor  az  ismét  lefut  hiba  nélkül:  Példaprogram  aktuális  állapotaA  példaprogram  futtatásából  látható  is,  hogy  most  az  új  DLL-t  használja  a  program,  mivel  megjelenik  a  Label-en  a  "version  2"  felirat  is.Ezzel  rábírtuk  tehát  az  EXE-t,  hogy  az  új  DLL-t  használja,  de  ha  minden  apró  változtatás  után  egy  fordításkor  ennyi  mindent  kell  tennünk,  hogy  kipróbálhassuk  programunk  aktuális  állapotát,  akkor  valószínűleg  hamar  lemondunk  a  GAC-ról.  Ennek  megoldására  hamarosan  visszatérünk,  de  most  tegyünk  egy  kis  kitérőt  és  elmélkedjünk  el  a  jelenlegi  helyzeten:Most  ott  van  a  GAC-ban  két  DLL.  Egyik  olyan  DLL  -  a  régebbi  verziójú  -  melyre  már  soha  nem  lesz  szükségünk.  A  másikat  használjuk.  Ha  a  régit  ott  hagyjuk,  akkor  idővel  igen  sok  DLL  összegyűlik.  Ezért  célszerű  ha  kitöröljük,  mielőtt  ellep  bennünket  ugyanazon  DLL  számtalan  verziója.  Ha  viszont  egy  valós  helyzetben  lévő  DLL-ről  van  szó,  amit  esetleg  több  alkalmazás  is  használ,  akkor  fontos  megbizonyosodni  a  törlés  előtt,  hogy  arra  a  DLL-re  nem  lesz  már  egyik  programnak  sem  szüksége.A  törlés  elvégzését  a  .NET  Framework  Configuration  alkalmazásban  végezhetjük  el.  Kattintsunk  jobb  gombbal  törlendő  DLL-en  és  válasszuk  a  Törlés  menüpontot.Most  ismét  csak  egy  DLL  van  csak  a  GAC-ban,  melyet  az  EXE-nk  használ.Ha  viszont  ismét  változtatni  kell  a  DLL  kódján,  akkor  ismét  másolhatjuk  be  a  GAC-ba  és  törölhetjük  a  régebbi  verziót.  Könnyen  belátható,  hogy  így  rövidesen  megbolondulnánk,  hiszen  egy  program  fejlesztésekor  számtalan  esetben  történik  kódolás  és  tesztképpen  futtatás.  Nem  lehet  minden  apró  változtatás  után  GAC-ba  másolni  és  régi  verziót  törölni.A  fejlesztés  idejére  tehát  célszerű  gondoskodni,  hogy  ne  a  GAC-ból  vegye  a  programunk  a  szükséges  DLL-t.Mivel  először  a  GAC-ban  keres  a  programunk,  így  töröljük  minden  addig  bemásolt  verziót  onnan.Majd  a  referenciának  a  Copy  Local  tulajdonságát  állítsuk  igazra:  Helyi  másolat  beállításaEzzel  újra  az  EXE  mellé  kerül  egy  másolat  a  DLL-ből  minden  fordításkor,  így  az  mindig  a  legfrisebb  DLL-t  használja.  Amikor  a  fejlesztés  olyan  szakaszba  ér,  hogy  átmenetileg  befejezettnek  tekinthető,  akkor  ráérnünk  a  DLL-t  a  GAC-ba  tenni.  Megint  más  a  helyzet  akkor,  ha  a  programot  nem  azon  a  gépen  használjuk,  ahol  fejlesztjük.  A  valóságban  ez  szokott  lenni  a  leggyakoribb  eset.  Ilyenkor  nem  is  érdemes  helyi  gépünkön  a  GAC  lehetőségeit  használni,  elegendő,  ha  a  felhasználónk  gépén  tesszük  csak  a  DLL-ket  a  GAC-ba.Miután  a  Copy  Local-t  igazra  állítottuk  tegyünk  egy  újabb  próbát,  de  előtte  írjuk  át  ismét  a  DLL-ben  lévő  függvényt  egy  kissé:        public  static  string  GetDateTime()        {            return  "version  3:  "  +                  DateTime.Now.ToString();        }Futtatáskor  látható,  hogy  a  módosítás  sikeres  volt:  Példaprogram  aktuális  állapotaA  DLL  fejlesztése  most  már  zökkenőmentesen  zajlik  és  a  kész  DLL-t  könnyedén  a  GAC-ba  helyezhetjük.  De  mi  a  helyzet  akkor,  ha  ezt  a  DLL-t  nem  csak  egy  alkalmazásunk  használná  fel???Webes  alkalmazás  hozzáadása  a  solution-hozTételezzük  fel,  hogy  kész  a  DLL  és  így  tegyük  be  a  GAC-ba.  Ezt  követően  hozzunk  létre  egy  új  webes  alkalmazást:  Új  webes  alkalmazás  létrehozásaEhhez  a  Solution  Exporer-ben  a  Solution  'GAC'  elemen  jobb  egérgombbal  kattintva  válasszuk  az  Add  -  New  Project  menüpontot.  ASP.NET  alkalmazás  készítéseA  megjelenő  ablakból  az  ASP.NET  Web  Application  tételt  választva,  hozzunk  létre  egy  GACWeb  nevű  alkalmazást.Miután  ez  is  létrejött,  adjuk  hozzá  a  GACTestLibrary  referenciát:  Referencia  hozzáadásaA  referencia  hozzáadása  a  szokásos  módon  a  .NET  lapon  a  GACTestLibrary.dll  előkeresésével  történjen.  Ezek  után  a  WebForm1.aspx.cs  forráskódban  már  hivatkozhatunk  a  GACTestLibrary-re.        using  GACTestLibrary;A  WebForm1-re  is  helyezzünk  egy  Label-t,  majd  a  lap  betöltődésekor  a  Page  Load  eseményénél  ismét  használjuk  fel  a  DLL-ben  lévő  függvényt  a  pontos  idő  megjelenítéséhez:        private  void  Page_Load(object  sender,              System.EventArgs  e)        {            Label1.Text  =  Class1.GetDateTime();        }Futtassuk  a  webes  alkalmazást:  A  webes  alkalmazás  aktuális  állapotaAmint  az  látható  eddig  minden  jól  megy,  jön  a  GAC-ból  a  DLL  és  szolgáltatja  az  adatot.Módosítsuk  újra  a  DLL-t:        public  static  string  GetDateTime()        {            return  "version  4:  "  +                  DateTime.Now.ToString();        }Majd  a  teljes  Solution-t  újrafordítva  és  futtatva,  a  webes  alkalmazásnál  is  előjön  a  hiba,  miszerint  nincs  megfelelő  verziójú  DLL:  DLL  verzió  miatti  hibaÚjra  másoljuk  a  GAC-ba  az  új  verziójú  DLL-t  a  régit  pedig  törölhetjük.  Ha  ekkor  újrafuttatjuk  a  webes  alkalmazást,  akkor  az  ismét  rendben  működik:  A  webes  alkalmazás  aktuális  állapotaVárakozásunknak  megfelelően  természetesen  egy  webes  alkalmazásnál  is  épp  úgy  előjönnek  a  verziószámok  miatti  problémák,  mint  a  Windows-os  alkalmazások  esetén.  Példaprogram  aktuális  állapotaHa  újrafordítjuk  a  Windows-os  alkalmazást  (Copy  Local  ismét  hamis  legyen!),  akkor  látható  a  futtatásnál,  hogy  az  is  a  GAC-ból  veszi  a  DLL-t  és  minden  jó,  minden  program  a  legújabb  DLL-t  használja.Ám  de...Ismét  felmerül  egy  probléma!Ha  azt  szeretnénk,  hogy  alkalmazásaink  mindig  a  legújabb  DLL-t  használják  és  ne  egy  régi  verziót,  akkor  a  DLL  újrafordításakor  nem  elegendő  azt  elhelyezni  a  GAC-ba,  hiszen  amíg  az  alkalmazást  nem  fordítottuk  le  újra,  addig  az  a  régi  DLL-t  keresi!  Most  ugye  már  két  programunk  van,  a  Windows-os  és  a  webes,  mely  ugyanazt  a  DLL-t  használja.  Ha  most  módosul  a  DLL,  akkor  kevés  azt  a  GAC-ba  tenni,  újra  kell  fordítanunk  mindkét  alkalmazásunkat  is.  Mondani  se  kell,  hogy  egy  DLL-t  nem  csak  két  alkalmazás  használhat,  hanem  számtalan.  Ha  arra  van  szükségünk,  hogy  minden  program  mindig  a  legfrissebb  DLL-t  vegye  elő  a  GAC-ból,  akkor  eléggé  fáradságos  munka  lenne  minden  programot  újrafordítani  és  újra  publikálni  azokat  a  felhasználóinkhoz.  Referencia  tulajdonságaiHa  jól  megfigyeljük  egy  programhoz  hozzáadott  referencia  tulajdonság  lapját,  akkor  láthatjuk  mi  is  problémánk  forrása.  A  tulajdonságok  között  ott  a  Version  property,  mely  eltárolja,  hogy  a  DLL  melyik  verzióját  kell  használni.  Ettől  kezdve  a  lefordított  program  mindig  ehhez  a  verzióhoz  ragaszkodik.  Amikor  változik  a  DLL  és  újrafordítjuk  magát  a  programunkat  is,  akkor  természetesen  ez  a  verziószám  automatikusan  az  aktuális  DLL  verziószámára  változik,  nincs  is  gondunk  rá.  Csakhogy  ehhez  újra  kell  fordítani  a  programunkat  és  újra  publikálni  azt,  ami  értelmetlen  tevékenység,  hiszen  adott  esetben  csupán  a  DLL  kódja  változik  és  az  EXE-hez  hozzá  sem  nyúltunk.Szerencsére  erre  a  problémára  is  találunk  megoldást  a  .Net-ben.  Minden  alkalmazáshoz  készíthetünk  egy  konfigurációs  állományt,  melyben  előírhatjuk,  hogy  mely  DLL,  mely  verzióját  használja  a  program.  Ha  alkalmazásunk  talál  ilyen  bejegyzést,  akkor  nem  veszi  figyelembe  azt,  hogy  a  program  fordításakor  mi  is  volt  a  "megjegyzett"  verziószám,  hanem  mindig  a  konfigurációs  állományban  megadott  verziószámú  DLL-t  keresi.  E  módszerrel  persze  nem  csak  arra  bírhatjuk  rá  programunkat,  hogy  egy  újabb  verziójú  DLL-t  használjon,  hanem  elérhetjük  akár  azt  is,  hogy  egy  régebbit.KonfigurációA  konfigurációs  állomány  teszteléséhez  módosítsuk  ismét  a  DLL-t:        public  static  string  GetDateTime(){            return  "version  5:  "  +                  DateTime.Now.ToString();        }Ezek  után  fordítsuk  le  újra  a  teljes  Solution-t!Futtassuk  a  Windows-os  programot:  hibajelzést  kapunk,  nincs  meg  a  megfelelő  DLL  verzió!Tegyük  be  az  újabb  DLL-t  is  GAC-ba,  ahol  most  már  két  verzió  lesz  jelen:  GAC  aktuális  állapotaFuttassuk  ismét  a  Windows-os  alkalmazást:  Példaprogram  aktuális  állapotaLátható,  hogy  az  új,  5-ös  verziót  használja  a  programunk.Most  bírjuk  rá  a  konfigurációs  állomány  segítségével  az  alkalmazásunkat,  hogy  ne  az  5-ös  verziót,  hanem  a  régebbi  4-es  verziót  használja,  mely  még  mindig  ott  található  a  GAC-ban.Most  hozzunk  létre  egy  TXT  típusú  állományt  azonos  névvel  mint  a  Windows-os  EXE,  de  .CONFIG  kiterjesztéssel:  GAC.exe.configEbbe  tesztképpen  írjuk  elő,  hogy  a  régebbi  verziójú  DLL-t  kell  használnia  programunknak,  mely  a  1.0.1688.16069  számon  fut.A  konfigurációs  állomány  nem  lesz  más,  mint  egy  XML  alapú  állomány,  melynek  tartalma  az  alábbi  legyen:<configuration>      <runtime>            <assemblyBinding  xmlns=                      "urn:schemas-microsoft-com:asm.v1">                  <dependentAssembly>                                                        Az  assemblyIdentity  bejegyzéssel  azonosíthatunk  egy  DLL-t.  A  name  attribútumba  kerül  a  neve,  a  publicKeyToken  a  publikus  kulcs  értéke  lesz,  melyet  a  Microsoft  .NET  Framework  Configuration  programban  megjelenő  GAC-ról  készült  listában  láthatunk  a  DLL-ünk  mellett.                        <assemblyIdentity                                name="GACTestLibrary"                                publicKeyToken="a73faccbc90c21c0"                                culture=""/>Most  már  megvan,  hogy  melyik  DLL-t  szeretnénk  konfigurálni,  így  már  csak  azt  kell  megmondanunk,  hogy  miképp.  Ehhez  egy  újabb,  bindingRedirect  bejegyzést  helyezünk  el.  Ennek  oldVersion  attribútumában  megadjuk,  hogy  mi  a  régebbi  verzió,  amelyet  szeretnénk  átirányítani.  Ha  ezt  nem  szeretnénk  állandóan  észben  tartani,  akkor  adjunk  meg  egy  verziószám  tartományt,  mely  magába  foglalja  az  összes  lehetséges  régebbi  verziószámot  és  így  tulajdonképpen  mindegy,  hogy  mi  is  az  aktuális  verzió:                        <bindingRedirect  oldVersion=                              "0.0.0.0-65535.65535.65535.65535"  A  newVersion  attribútumban  pedig  annak  a  DLL-nek  a  verziószámát  adjuk  meg,  melyet  szeretnénk,  ha  használna  a  programunk.  Ez  most  a  régebbi  4-es  verzió  feliratú  DLL  lesz,  melynek  verziószáma:  1.0.1688.16069.                              newVersion="1.0.1688.16069"/>                  </dependentAssembly>                                                                                                                                                        </assemblyBinding>      </runtime></configuration>A  konfigurációs  állomány  mindig  az  EXE  mellett  kell  hogy  legyen  és  amikor  publikáljuk  a  programunkat,  akkor  erre  az  állományra  is  szükség  lesz  a  felhasználónknál.Most  futtassuk  ismét  az  EXE-t  minden  módosítás  nélkül:  Példaprogram  aktuális  állapotaLátható  lesz,  hogy  a  program  ismét  a  régi  verziójú  DLL-t  használja,  hiszen  ismét  a  4-es  verzió  felirat  jelenik  meg  és  nem  a  legfrissebb  5-ös.  A  tesztből  kiderül  tehát,  hogy  tetszőleges  verziójú  DLL  használatára  késztethetjük  az  alkalmazást,  így  amikor  egy  EXE-be  belefordítunk  egy  adott  verziójú  DLL  használatát  és  idővel  módosítjuk  a  DLL-t,  új  verziót  publikálunk  a  GAC-ba,  akkor  nem  kell  az  EXE-t  is  újrafordítani,  elég  csak  a  konfigurációs  állományba  megadni  az  új  verziószámot  és  attól  kezdve  az  a  DLL  lesz  használva.Természetesen  webes  alkalmazásnál  is  járható  ez  az  út,  csak  ott  a  web.config  állományt  kell  konfigurációs  állományként  használni.VakációVégére  értünk  hát  a  GAC  körüli  utazásunknak.  Láthattuk,  hogy  minden  jóban  van  valami  rossz  is,  de  összességében  levonható  következtetés,  hogy  érdemes  használni  a  GAC  lehetőségeit,  persze  csak  a  végfelhasználók  gépén  és  nem  azon,  ahol  a  programot  fejlesztjük.  Prethus  Gábor