Hey,
Ik ben een programmaatje aan het schrijven waarin men VB en C# source code bestanden kan openen. Het is een soort van 'lightweight' visual studio IDE zonder de meeste 'zware' dingen die de IDE langzaam maken met opstarten en dergelijke. De bedoeling is dat men snel even een source file kan bekijken of kan aanpassen zonder de hele IDE te hoeven openen (of zelfs geinstalleerd te hoeven hebben). Ik zelf maak vaak genoeg mee dat ik een source file even snel in notepad (of notepad++ nog beter natuurlijk) open in plaats van dat ik de moeite neem om de IDE te starten.
Om een lang verhaal enigzins kort te houden: toch wil ik graag een kleinschalig compilen of parsen van de code inbouwen. Het idee uiteindelijk is dat men een solution file, vb project of C# project kunnen openen (dit werkt al), waarna men niet alleen de files krijgt in een 'solution explorer' (gewoon een treeview met alle files) maar veel belangrijker: ook een soort van Intellisense support (voor zover mogelijk) en een 'class view' window, met een treeview met de types en hun members.
Ik heb een tijd lang gezocht naar manieren om VB en C# code te parsen (daarmee bedoel ik: code inlezen en bepalen welke types er gedeclareerd zijn en welke members ze hebben, etc, zodat ik een class view kan opbouwen), maar dat lijkt niet op een handige manier mogelijk te zijn behalve handmatig een parser te schrijven. Dat heb ik nog nooit gedaan dus dat zal echt een 'last resort' zijn. Toch: als er hier goeie documentatie over bestaan zou ik dat graag weten. Ik kwam een tijd geleden toevallig een document (vast op MSDN) tegen waar de VB en C# talen exact uitgelegd werden, welk format ze hadden en wanneer welke 'token' geldig was, zo'n soort dingen. Maar die kan ik helaas niet meer vinden...
Anyway, ik ben dus op zoek geweest naar slimme manieren om dit op te lossen, of nog beter: het door .NET zelf te laten doen. In de System.CodeDom(.Compiler) namespaces lijken een groot aantal classes te zijn die met compilen en parsen te maken hebben. Maar voor zover ik kan zien is er steeds maar een method (CodeParser.Parse) die aan parsen (in tegenstelling tot compilen) doet, en die is bij geen enkele class geimplementeerd...
Toen bedacht ik echter een 'omweg' die zeker weten mogelijk is, maar misschien niet ideaal is. Ik zou natuurlijk de source code kunnen compilen (System.CodeDom.Compiler.CodeProvider.CompileAssemblyFromSource) en daarna met Reflection de assembly terug uit kunnen lezen. Daarmee krijg ik alle types en hun members 'kado'. Dat is natuurlijk niet ideaal: voor het compilen zal vast en zeker ook een soort van parser zijn werk doen dus ben ik alles eigenlijk dubbel aan het doen, maar behalve alles handmatig te doen lijkt dit de enige optie te zijn.
Anyway, dat is dus wat ik nu aan het proberen ben. Als men een project file in laadt dan sla ik een aantal dingen op (die lees ik uit de project xml file) namelijk:
- De files in het project
- De References (referenced assemblies) in het project
- De Imports (in het geval van VB kunnen er "impliciet" geimporteerde namespaces zijn die niet in de source code voorkomen).
Ik probeer nu de source files in dit project te compileren, maar voor de meeste projecten gaat het hopeloos mis... Dit is de code die ik gebruik:
Voor de meest simpele C# projecten gaat dit goed. Ik krijg nu een assembly die ik met Reflection kan inspecteren en daaruit kan ik een mooie treeview opbouwen.
Maar bij elk VB project gaat het mis. Ik heb eigenlijk twee problemen:
1. Ik weet niet hoe ik de 'impliciete' imports moet behandelen, en
2. Ik krijg errors die ik nooit eerder heb gezien, die niet in een C# project voorkomen en ik heb geen idee hoe ik ze oplos.
Voor puntje 1:
In de XML file kan ik een aantal imports statements uitlezen die in VB altijd 'verborgen' zijn. Ze staan niet fysiek in de source code maar bij de compilatie worden ze wel gebruikt. Een voorbeeld is de System.Drawing namespace, die in een winforms project automatisch geimporteerd wordt. Je kan daarom altijd een Color of een Rectangle oid gebruiken zonder 'Imports System.Drawing' boven de code te zetten. Maar als ik deze code nu laat compileren met mijn code dan werkt het natuurlijk niet: deze compiler heeft geen idee van deze verborgen geimporteerde namespaces dus kan hij Color, Rectangle, etc niet vinden.
Ik heb nu wel de imports statements die ik boven de code moet zetten, dus ik zou het in principe handmatig kunnen doen door de source steeds handmatig aan te passen, maar ik zit nu met twee problemen:
- De imports staan niet altijd bovenaan. Als er een 'Option' statement is dan moeten ze daar nog onder,
- Je mag niet dezelfde import meerdere keren gebruiken, dan gaat het ook mis.
Dus ik moet de source weer zelf gaan lezen om te kijken welke imports statements er al zijn, en waar de 'hidden' imports toegevoegd moeten worden. Dat is niet echt de bedoeling want dat is precies wat ik probeer te vermijden... Kan dit niet op een betere manier?
Voor puntje 2, dit zijn twee voorbeelden van de errors die ik krijg:
Dus, wat ik eigenlijk vraag is: hoe compile je zonder problemen een heel project? Ik heb uiteraard zitten googlen en op MSDN gekeken, maar eigenlijk vind ik alleen maar voorbeelden om kleine 'scriptjes' te compilen, zoals een mathematische expressie die men dan dynamisch kan intypen ofzo. Dat werkt allemaal prima, maar zodra ik een heel project ga compilen kom ik dus deze problemen tegen...
Er moet hier toch wel meer en betere informatie over te vinden zijn? Vooral over deze 'implicit imports' in VB: dat komt iedereen toch tegen die een vb project wil compilen in code? Toch kan ik niets vinden... Misschien zoek ik met de verkeerde keywords.
Weet iemand hier iets meer van? Bedankt!
Ik ben een programmaatje aan het schrijven waarin men VB en C# source code bestanden kan openen. Het is een soort van 'lightweight' visual studio IDE zonder de meeste 'zware' dingen die de IDE langzaam maken met opstarten en dergelijke. De bedoeling is dat men snel even een source file kan bekijken of kan aanpassen zonder de hele IDE te hoeven openen (of zelfs geinstalleerd te hoeven hebben). Ik zelf maak vaak genoeg mee dat ik een source file even snel in notepad (of notepad++ nog beter natuurlijk) open in plaats van dat ik de moeite neem om de IDE te starten.
Om een lang verhaal enigzins kort te houden: toch wil ik graag een kleinschalig compilen of parsen van de code inbouwen. Het idee uiteindelijk is dat men een solution file, vb project of C# project kunnen openen (dit werkt al), waarna men niet alleen de files krijgt in een 'solution explorer' (gewoon een treeview met alle files) maar veel belangrijker: ook een soort van Intellisense support (voor zover mogelijk) en een 'class view' window, met een treeview met de types en hun members.
Ik heb een tijd lang gezocht naar manieren om VB en C# code te parsen (daarmee bedoel ik: code inlezen en bepalen welke types er gedeclareerd zijn en welke members ze hebben, etc, zodat ik een class view kan opbouwen), maar dat lijkt niet op een handige manier mogelijk te zijn behalve handmatig een parser te schrijven. Dat heb ik nog nooit gedaan dus dat zal echt een 'last resort' zijn. Toch: als er hier goeie documentatie over bestaan zou ik dat graag weten. Ik kwam een tijd geleden toevallig een document (vast op MSDN) tegen waar de VB en C# talen exact uitgelegd werden, welk format ze hadden en wanneer welke 'token' geldig was, zo'n soort dingen. Maar die kan ik helaas niet meer vinden...
Anyway, ik ben dus op zoek geweest naar slimme manieren om dit op te lossen, of nog beter: het door .NET zelf te laten doen. In de System.CodeDom(.Compiler) namespaces lijken een groot aantal classes te zijn die met compilen en parsen te maken hebben. Maar voor zover ik kan zien is er steeds maar een method (CodeParser.Parse) die aan parsen (in tegenstelling tot compilen) doet, en die is bij geen enkele class geimplementeerd...
Toen bedacht ik echter een 'omweg' die zeker weten mogelijk is, maar misschien niet ideaal is. Ik zou natuurlijk de source code kunnen compilen (System.CodeDom.Compiler.CodeProvider.CompileAssemblyFromSource) en daarna met Reflection de assembly terug uit kunnen lezen. Daarmee krijg ik alle types en hun members 'kado'. Dat is natuurlijk niet ideaal: voor het compilen zal vast en zeker ook een soort van parser zijn werk doen dus ben ik alles eigenlijk dubbel aan het doen, maar behalve alles handmatig te doen lijkt dit de enige optie te zijn.
Anyway, dat is dus wat ik nu aan het proberen ben. Als men een project file in laadt dan sla ik een aantal dingen op (die lees ik uit de project xml file) namelijk:
- De files in het project
- De References (referenced assemblies) in het project
- De Imports (in het geval van VB kunnen er "impliciet" geimporteerde namespaces zijn die niet in de source code voorkomen).
Ik probeer nu de source files in dit project te compileren, maar voor de meeste projecten gaat het hopeloos mis... Dit is de code die ik gebruik:
Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| Private Shared Function CompileSource(ByVal language As Languages, _ ByVal sources As IEnumerable(Of String), _ ByVal references As IEnumerable(Of String)) As Assembly ' Get the CodeDomProvider used to compile Dim provider = GetCodeDomProvider(language) ' Set up some parameters Dim params As New CompilerParameters() params.GenerateInMemory = True params.GenerateExecutable = False ' Add the referenced assemblies found in the project xml file params.ReferencedAssemblies.AddRange(references.ToArray()) ' Finally compile the sources and return the compiled assembly Dim result = provider.CompileAssemblyFromSource(params, sources.ToArray()) Return result.CompiledAssembly End Function |
Voor de meest simpele C# projecten gaat dit goed. Ik krijg nu een assembly die ik met Reflection kan inspecteren en daaruit kan ik een mooie treeview opbouwen.
Maar bij elk VB project gaat het mis. Ik heb eigenlijk twee problemen:
1. Ik weet niet hoe ik de 'impliciete' imports moet behandelen, en
2. Ik krijg errors die ik nooit eerder heb gezien, die niet in een C# project voorkomen en ik heb geen idee hoe ik ze oplos.
Voor puntje 1:
In de XML file kan ik een aantal imports statements uitlezen die in VB altijd 'verborgen' zijn. Ze staan niet fysiek in de source code maar bij de compilatie worden ze wel gebruikt. Een voorbeeld is de System.Drawing namespace, die in een winforms project automatisch geimporteerd wordt. Je kan daarom altijd een Color of een Rectangle oid gebruiken zonder 'Imports System.Drawing' boven de code te zetten. Maar als ik deze code nu laat compileren met mijn code dan werkt het natuurlijk niet: deze compiler heeft geen idee van deze verborgen geimporteerde namespaces dus kan hij Color, Rectangle, etc niet vinden.
Ik heb nu wel de imports statements die ik boven de code moet zetten, dus ik zou het in principe handmatig kunnen doen door de source steeds handmatig aan te passen, maar ik zit nu met twee problemen:
- De imports staan niet altijd bovenaan. Als er een 'Option' statement is dan moeten ze daar nog onder,
- Je mag niet dezelfde import meerdere keren gebruiken, dan gaat het ook mis.
Dus ik moet de source weer zelf gaan lezen om te kijken welke imports statements er al zijn, en waar de 'hidden' imports toegevoegd moeten worden. Dat is niet echt de bedoeling want dat is precies wat ik probeer te vermijden... Kan dit niet op een betere manier?
Voor puntje 2, dit zijn twee voorbeelden van de errors die ik krijg:
Type 'HatchBrushTest.My.MySettings' is not defined.
'HatchBrushTest' is de naam van het project (en dus ook de default namespace). Ik denk dat daar het probleem zit: met de default namespace. Weer iets wat wel in VB zit maar niet in C#. Maar ik heb geen idee hoe ik dit moet oplossen...? Moet ik alle source code in "Namespace <project naam>" tags gaan wikkelen? Dan moet ik dus alweer zelf de source gaan lezen, en alweer: dat doe ik liever niet als het niet anders kan...'HatchBrushTest' is not a member of '<Default>'.
Dus, wat ik eigenlijk vraag is: hoe compile je zonder problemen een heel project? Ik heb uiteraard zitten googlen en op MSDN gekeken, maar eigenlijk vind ik alleen maar voorbeelden om kleine 'scriptjes' te compilen, zoals een mathematische expressie die men dan dynamisch kan intypen ofzo. Dat werkt allemaal prima, maar zodra ik een heel project ga compilen kom ik dus deze problemen tegen...
Er moet hier toch wel meer en betere informatie over te vinden zijn? Vooral over deze 'implicit imports' in VB: dat komt iedereen toch tegen die een vb project wil compilen in code? Toch kan ik niets vinden... Misschien zoek ik met de verkeerde keywords.
Weet iemand hier iets meer van? Bedankt!