C# - A parancssori fordító program használata (CSC.EXE )

forráskód letöltése
A .NET Framework sok egyéb mellett rendelkezik egy parancssori fordító segédprogrammal, melynek neve CSC.EXE. Számtalan parancssori opcióval rendelkezik annak érdekében, hogy a fordítás igazán testre szabható legyen. Cikkünkben ezek használatából mutatunk be néhányat. A mellékelt példában programból meghívva is kipróbálhatjuk a segédprogramot, egy megadott forrás fordítására.
A parancssori fordítóval (CSharp Compiler) tehát tetszőleges forrásállomány(ok)ból generálható futtatható alkalmazás, vagy dinamikus könyvtár (DLL). A parancssori fordító alapértelmezésben a Windows mappájában található, a következő elérési útvonalon:
<winroot>:\\Windows\Microsoft.NET\Framework\<.NET Framework verziószámától függő mappanév>\csc.exe. A Visual Studio.NET Command Promptját elindítva bárhonnan elérhető.
Vizsgáljuk meg a parancssori opciókat sorban:
/?
Megjeleníti a parancssori opciókat, melyeket használhatunk.
/addmodule: modul neve
Megadható egy metaadatokat tartalmazó állomány neve, mely nem tartalmaz assembly manifeszt-et. Több állomány esetén azok nevét pontosvesszővel kell elválasztani egymástól.
A megadandó állományoknak a kimenettel azonos mappában kell lenniük futáskor, fordítási időben természetesen tetszőleges elérési útvonalak specifikálhatóak. Ellenkező esetben egy System.TypeLoadException típusú kivétel generálódik.
Ha egy forrásállomány a /target: module opcióval van lefordítva, akkor a kimenet beszerkeszthető a kódba az /addmodule opcióval.
/baseaddress: cím
Megadható a DLL kezdőcíme hexadecimális, oktális, vagy decimális formátumban. Az alapértelmezett megoldás, hogy a CLR ad kezdőcímet a DLL-nek. Ha a kimenet nem DLL, akkor ez az opció figyelmen kívül marad.
/bugreport: fájl neve
Fordításkor egy állományba kerül a lista az esetleges hibákról. A hibaállomány a következőket tartalmazza: fordítási opciók, forráskódok, verzióinformációk (operációs rendszer, CLR, és fordító adatai), generált kimenet, hibajegyzés, és javaslat a megoldásról. Egy példa erre:
csc /bugreport:problem.txt t2.cs
/codepage: azonosító
Megadható a fordításkor alkalmazandó kódlap azonosítója.
/debug [+/-]
A fordításkor DEBUG információk is keletkeznek egy állományban, ez a keretrendszer DEBUG módú fordításával azonos funkció. Egy példa a használatára:
csc /debug /out:app.exe test.cs
/define: definíció neve
Megadható, hogy milyen szimbólumokat szeretnénk definiálni a fordításkor. Egyenértékű azzal, ha egy #define preprocesszor direktívát használunk a forrásban. Használható definíciók: #if, #elif, #else és #endif, melyekkel testre szabható a fordítás. Több szimbólum definíciója esetén azok nevét pontosvesszővel választjuk el. Egy kódrészlet, mely szemlélteti a leírtakat:
#define xx
using System;
public class Test 
{
  public static void Main() 
  {
    #if (xx) 
      Console.WriteLine("xx exists");
    #else
      Console.WriteLine("xx does not exist");
    #endif
  }
}
/doc: fájlnév
A forráskód kiegészítéseit helyezhetjük el egy XML állományban. A definíciók, melyeket megelőz a „///” karakterliterál, bekerülnek az állományba. A forrás, mely a Main függvényt tartalmazza, elsőként kerül be az állományba.
/incremental [+/-]
A fordítási opció használatával engedélyezhetjük vagy tilthatjuk a fordító számára a fordítás azon típusát, melyben csak a megváltozott alkotókat építi újra. Ekkor a következő állományokban tárolódnak a fordítás előző végrehajtásával összefüggő adatok:
  • kimenet_neve.dbg: /debug opció használatakor a DEBUG státusz információi a program adatbázisban tárolódnak (.pdb).
  • kimenet_neve.kiterjesztése.incr: a fordítási információk, melyek eltérnek a DEBUG információktól, egy .incr állományban tárolódnak.
Amikor első alkalommal használjuk az /incremental opciót, akkor generálódnak a fenti állományok, majd az opciók megváltoztatása eredményez egy teljes újrafordítást. Ha a fordító sok változást észlel az utolsó fordítás óta, vagy nem találja a fenti állományokat, akkor is egy újrafordítás következik be.
Az /incremental opcióval fordított kimenet állományai nagyobb méretűek.
/lib: könyvtár
A /reference opcióban megadott hivatkozott assembly-k könyvtárát lehet itt megadni, amennyiben azok nincsenek a kimenet mappájában, vagy a CLR rendszermappájában. A fordító a következő sorrendben keresi a hivatkozott assembly-ket:
  • fordító mappája
  • CLR rendszermappája
  • /lib opcióval megadott mappa
  • LIB környezeti változóban megadott mappa
Használatára a következő példát említhetnénk:
csc /lib:c:\ /reference:t2.dll t2.cs
/linkresource: fájlnév
Megadható egy erőforrás-állomány, mely a fordítandó kódhoz szerkesztendő. Az erőforrás-állomány nincs a kimenet mappájában. Az opció használatához a generált kimenet .EXE, vagy .DLL állomány kell, hogy legyen. Ha olyan erőforrásról van szó, mely a fejlesztőkörnyezetben generálódott, vagy a RESGEN.EXE segédprogrammal jött létre, akkor azt a System.Resources névtér elemeivel lehet elérni. Minden egyéb eredetű erőforrás-állományt a System.Reflection.Assembly osztály metódusaival tudunk elérni futási időben.
Ha adott egy .RESOURCE állomány, akkor a következőképpen használhatjuk fel a fordításkor:
csc /linkresource:rf.resource in.cs
/main: osztály neve
Megadható, hogy a Main függvény deklarációja mely osztályban található. Ha több olyan állományt fordítunk, mely tartalmaz Main deklarációt, meg kell adnunk, hogy melyik legyen a kezdőpont. Csak .EXE állomány generálásakor használható. Például:
csc t2.cs t3.cs /main:Test2
/nostdlib[+/-]
Megadható, hogy a mscorlib.dll ne importálódjon a fordításkor. A típus a System névtérben található. Akkor alkalmazzuk, ha saját System névteret akarunk létrehozni.
/nowarn: figyelmeztetés száma
A fordító számára jelezhetjük, hogy mely azonosítóval rendelkező figyelmeztetés ne jelenjen meg a képernyőn. Ha például a CS0028 figyelmeztetést nem kívánjuk megjeleníteni, akkor a következőképpen kell használnunk az opciót:
Csc /nowarn:28 test.cs
/optimize [+/-]
A generált kimenet optimalizálható sebességre, hatékonyságra és méretre. Ezek arányai szabhatók meg az opció használatával. Arra figyelni kell, hogy a hivatkozott assembly-k azonos módon legyenek optimalizálva. Használata egyszerű:
csc t2.cs /optimize
/out : fájlnév
A kimenet neve adható meg az opcióval. Ha nem adunk meg /out opciót, akkor .EXE esetén a Main definíciót tartalmazó modul neve lesz a generált állomány neve, .DLL, vagy .NETMODULE esetén az első forrásállomány neve.
Fordításkor több generált kimenet is specifikálható. Ekkor a források neveit az /out opció után kell feltüntetni, mindegyiket elkülönítve. A következő példában két forrásállományt használunk, és mindkettőt külön állományba szeretnénk fordítani. Ekkor az alábbi a teendőnk:
csc t2.cs /out:t3.exe t3.cs
/recurse: könyvtárnév\fájlnév
Az opciót több forrásállománnyal rendelkező projekt esetén alkalmazhatjuk, mert megadható, hogy egy mappa összes almappájában található állomány fordításra kerüljön. Ha például a projekt mappájában található összes .CS állományát le kívánjuk fordítani, a következőt kell megadnunk:
csc *.cs
Ha viszont a források egy alternatív mappában tárolódnak, akkor van szükségünk a /recurse opcióra.
csc /target:library /out:dir2.dll /recurse: dir1\dir2\*.cs
/reference: fájlnév
Megadható azoknak a hivatkozott assembly-knek a listája, melyeket be kell szerkesztenünk a kódba. Előfordulhat olyan eset is, hogy hivatkozunk egy assembly-re (A), mely további assembly-kre (B) hivatkozik. Ekkor a további assembly-ket csak a következő esetekben kell felsorolnunk:
  • Ha az általunk hivatkozott assembly-ben (A) megtalálható típus a B assembly-ben megtalálható típusból származik,
  • Ha az A-ban meghívott metódus visszatérési értékének típusa a B assembly-ben van definiálva.
Példaként a következő parancssort említhetnénk:
Csc /reference:System.dll test.cs
/resource: állománynév
A megadott erőforrás állomány a generált kódba szerkesztődik be, így az erőforrást nem kell mozgatnunk a futtatható állománnyal együtt, ha azt egy másik gépre telepítjük. Használata igen egyszerű:
csc /resource:rf.resource in.cs
/target: kulcsszó
Megadható, hogy a fordítás milyen típusú kimenetet generáljon. A lehetséges típusok (kulcsszó értékek) a következők:
  • exe: Ekkor futtatható .EXE állomány generálódik,
  • library: .DLL állomány generálódik,
  • module: Modul generálódik,
  • winexe: Windows-os .EXE generálódik.
Az assembly manifeszt – melyből csak egy generálódik fordításonként – az első generált .EXE állományban található, vagy az első .DLL-ben, ha nincs .EXE állomány. Minden forrásfájlokkal kapcsolatos információ a manifeszt-ben tárolódik.
/unsafe
Jelezhetjük a fordító számára, hogy a forrás UNSAFE kulcsszót használ. Használata:
csc /unsafe in.cs
/warn: szint
Beállítható a figyelmeztetési szint, melynek megfelelően üzenetek jelennek meg a fordítással kapcsolatban. A skála 0 és 4 közötti értékeket tartalmaz, ahol a 0 szint esetén nem jelenik meg semmilyen üzenet. Használata:
Csc /warn:1 test.cs
/warnaserror [+/-]
Specifikálható, hogy valamennyi WARNING figyelmeztetés ERROR-ként jelenjen meg, vagyis eredményezze a fordítási folyamat leállását, és akadályozza meg kimenet generálását. Használata:
Csc /warnaserror test.cs
/win32icon: fájlnév
Megadható egy, a forrásba beszerkesztendő ikon állomány elérési útvonala. Ekkor az alkalmazás neve mellett ez az ikon jelenik meg a fájlkezelő alkalmazásokban. Használata:
csc /win32icon:rf.ico in.cs
/win32res: fájlnév
Megadható egy, a forrásba beszerkesztendő erőforrás-állomány elérési útvonala. Szintén az alkalmazás neve mellett megjelenő kép szerkesztődik be ily módon. Az állomány .RES kiterjesztésű. Használata:
csc /win32res:rf.res in.cs
Mellékelt példa
A mellékelt példa három projektet tartalmaz. A központi szerep a CLibrary projekté, mely egy .DLL-t generál, benn egy statikus metódussal. A metódus visszaad egy karakterláncot, mely tartalmazza azt a karakterláncot, melyet bemenő paraméterként megkapott, megtoldva egy bevezető szöveggel.
A CClient konzolalkalmazás felhasználja a CLibrary.dll állományt a hagyományos módon, hogy szemléltessük annak működését.
A CWinClient alkalmazás viszont nem szerkeszti be a DLL-t, hanem programból lefordítja azt, majd meghívja annak metódusát. A DLL kódját tartalmazó CClass.cs állományt a futtatandó alkalmazás mappájában helyeztük el, majd ide kerül a DLL is.
A program betöltődésekor megkeressük a CSC.EXE állományt a merevlemezen, majd elérési útvonalát elhelyezzük a Form egyik szövegmezőjében.
string cspath = Directory.GetParent(System.Environment.SystemDirectory).FullName + "\\Microsoft.NET\\Framework\\";
string[] dirs = Directory.GetDirectories(cspath);
DirectoryInfo di = new DirectoryInfo(dirs[0]);
cspath += di.Name + "\\csc.exe";
textBox2.Text = cspath;
Amennyiben az útvonal megfelelő, akkor a Process osztály segítségével meghívjuk azt a megfelelő parancssori argumentumokkal. Ehhez szükséges egy ProcessStartInfo típusú objektum.
ProcessStartInfo psi = new ProcessStartInfo();
Majd meg kell adnunk a hívandó állomány nevét.
psi.FileName = textBox2.Text;
Az opciók a következők, mellyel meghívjuk a programot:
psi.Arguments = "/target:library /reference:System.dll /out:CLibrary.dll CClass.cs";
Vagyis: <elérési útvonal>\csc.exe "/target:library /reference:System.dll /out:CLibrary.dll CClass.cs. Végül futtatjuk a Start metódust.
Process.Start(psi);
A Futtatás gombra kattintva elérhetjük a DLL metódusát. Készítünk egy assembly objektumot, majd kinyerjük a CClass típust.
Assembly a = Assembly.LoadFrom(Application.StartupPath + "\\CLibrary.dll");
Type t = a.GetType("CLibrary.CClass");
Ezt példányosítjuk, majd készítünk egy objektum-tömböt a metódus egy paraméterének, melynek értéke a második szövegmezőben megadott karakterliterál.
object obj = Activator.CreateInstance(t);
object[] p = new object[]{textBox1.Text};
Végül meghívjuk a GetText metódust.
...
t.InvokeMember("GetText",BindingFlags.InvokeMethod,null,obj,p)
...