1010using System . Threading ;
1111using System . Threading . Tasks ;
1212
13+ using ICSharpCode . BamlDecompiler ;
1314using ICSharpCode . Decompiler ;
1415using ICSharpCode . Decompiler . CSharp ;
1516using ICSharpCode . Decompiler . CSharp . ProjectDecompiler ;
@@ -52,6 +53,15 @@ ilspycmd sample.dll --generate-diagrammer
5253 Generate a HTML diagrammer containing filtered type info into a custom output folder
5354 (including types in the LightJson namespace while excluding types in nested LightJson.Serialization namespace)
5455 ilspycmd sample.dll --generate-diagrammer -o c:\diagrammer --generate-diagrammer-include LightJson\\..+ --generate-diagrammer-exclude LightJson\\.Serialization\\..+
56+
57+ List all embedded resources in a WPF assembly (including BAML entries inside .g.resources containers).
58+ ilspycmd sample.dll --list-resources
59+
60+ Extract a single resource. If the name ends with .baml, the output is decompiled XAML; otherwise raw bytes.
61+ ilspycmd sample.dll --resource sample.g.resources/mainwindow.baml -o c:\decompiled
62+
63+ Decompile assembly as a compilable project and convert all BAML resources to XAML Page items.
64+ ilspycmd sample.dll -p -o c:\decompiled --decompile-baml
5565" ) ]
5666 [ HelpOption ( "-h|--help" ) ]
5767 [ ProjectOptionRequiresOutputDirectoryValidation ]
@@ -93,6 +103,15 @@ class ILSpyCmdProgram
93103 [ Option ( "-l|--list <entity-type(s)>" , "Lists all entities of the specified type(s). Valid types: c(lass), i(nterface), s(truct), d(elegate), e(num)" , CommandOptionType . MultipleValue ) ]
94104 public string [ ] EntityTypes { get ; } = Array . Empty < string > ( ) ;
95105
106+ [ Option ( "--list-resources" , "Lists all embedded resources in the assembly. Entries inside .resources containers are listed individually as '<container>/<entry>'." , CommandOptionType . NoValue ) ]
107+ public bool ListResourcesFlag { get ; }
108+
109+ [ Option ( "--resource <name>" , "Extract a single resource by name (as printed by --list-resources). Resources whose name ends with '.baml' are decompiled to XAML." , CommandOptionType . SingleValue ) ]
110+ public string ResourceName { get ; }
111+
112+ [ Option ( "--decompile-baml" , "When used with -p, decompile BAML resources to XAML files (Page items) instead of leaving them as raw byte streams." , CommandOptionType . NoValue ) ]
113+ public bool DecompileBamlFlag { get ; }
114+
96115 public string DecompilerVersion => "ilspycmd: " + typeof ( ILSpyCmdProgram ) . Assembly . GetName ( ) . Version . ToString ( ) +
97116 Environment . NewLine
98117 + "ICSharpCode.Decompiler: " +
@@ -257,8 +276,8 @@ private async Task<int> OnExecuteAsync(CommandLineApplication app)
257276 var checkResult = await updateCheckTask ;
258277 if ( null != checkResult && checkResult . UpdateRecommendation )
259278 {
260- Console . WriteLine ( "You are not using the latest version of the tool, please update." ) ;
261- Console . WriteLine ( $ "Latest version is '{ checkResult . LatestVersion } ' (yours is '{ checkResult . RunningVersion } ')") ;
279+ app . Error . WriteLine ( "You are not using the latest version of the tool, please update." ) ;
280+ app . Error . WriteLine ( $ "Latest version is '{ checkResult . LatestVersion } ' (yours is '{ checkResult . RunningVersion } ')") ;
262281 }
263282 }
264283 }
@@ -306,6 +325,20 @@ int PerformPerFileAction(string fileName)
306325 {
307326 return DumpPackageAssemblies ( fileName , outputDirectory , app ) ;
308327 }
328+ else if ( ListResourcesFlag )
329+ {
330+ if ( outputDirectory != null )
331+ {
332+ string outputName = Path . GetFileNameWithoutExtension ( fileName ) ;
333+ output = File . CreateText ( Path . Combine ( outputDirectory , outputName ) + ".resources.txt" ) ;
334+ }
335+
336+ return ListResources ( fileName , output ) ;
337+ }
338+ else if ( ResourceName != null )
339+ {
340+ return ExtractResource ( fileName , ResourceName , output , outputDirectory , app ) ;
341+ }
309342 else
310343 {
311344 if ( outputDirectory != null )
@@ -430,6 +463,85 @@ int ListContent(string assemblyFileName, TextWriter output, ISet<TypeKind> kinds
430463 return 0 ;
431464 }
432465
466+ int ListResources ( string assemblyFileName , TextWriter output )
467+ {
468+ var module = new PEFile ( assemblyFileName ) ;
469+ foreach ( var path in ResourceExtensions . EnumerateResourcePaths ( module ) )
470+ {
471+ output . WriteLine ( path ) ;
472+ }
473+ return 0 ;
474+ }
475+
476+ int ExtractResource ( string assemblyFileName , string resourceName , TextWriter output , string outputDirectory , CommandLineApplication app )
477+ {
478+ var module = new PEFile ( assemblyFileName ) ;
479+ if ( ! ResourceExtensions . TryGetResource ( module , resourceName , out object value ) )
480+ {
481+ app . Error . WriteLine ( $ "Resource '{ resourceName } ' not found.") ;
482+ app . Error . WriteLine ( "Available resources:" ) ;
483+ foreach ( var p in ResourceExtensions . EnumerateResourcePaths ( module ) )
484+ app . Error . WriteLine ( " " + p ) ;
485+ return ProgramExitCodes . EX_DATAERR ;
486+ }
487+
488+ bool isBaml = resourceName . EndsWith ( ".baml" , StringComparison . OrdinalIgnoreCase ) ;
489+ if ( isBaml && value is byte [ ] bamlBytes )
490+ {
491+ var resolver = new UniversalAssemblyResolver ( assemblyFileName , false , module . Metadata . DetectTargetFrameworkId ( ) ) ;
492+ foreach ( var path in ( ReferencePaths ?? Array . Empty < string > ( ) ) )
493+ resolver . AddSearchDirectory ( path ) ;
494+ var bamlSettings = new BamlDecompilerSettings {
495+ ThrowOnAssemblyResolveErrors = GetSettings ( module ) . ThrowOnAssemblyResolveErrors
496+ } ;
497+
498+ using var bamlStream = new MemoryStream ( bamlBytes ) ;
499+ var xaml = ResourceExtensions . DecompileBaml ( module , resolver , bamlStream , bamlSettings , CancellationToken . None ) ;
500+ if ( outputDirectory != null )
501+ {
502+ string xamlFile = WholeProjectDecompiler . SanitizeFileName ( Path . GetFileNameWithoutExtension ( resourceName ) + ".xaml" ) ;
503+ string fullPath = Path . Combine ( outputDirectory , xamlFile ) ;
504+ xaml . Save ( fullPath ) ;
505+ }
506+ else
507+ {
508+ output . Write ( xaml . ToString ( ) ) ;
509+ }
510+ return 0 ;
511+ }
512+
513+ if ( value is byte [ ] binary )
514+ {
515+ if ( outputDirectory != null )
516+ {
517+ string fileName = WholeProjectDecompiler . SanitizeFileName ( Path . GetFileName ( resourceName ) ) ;
518+ string fullPath = Path . Combine ( outputDirectory , fileName ) ;
519+ File . WriteAllBytes ( fullPath , binary ) ;
520+ }
521+ else
522+ {
523+ var stdout = Console . OpenStandardOutput ( ) ;
524+ stdout . Write ( binary , 0 , binary . Length ) ;
525+ stdout . Flush ( ) ;
526+ }
527+ }
528+ else
529+ {
530+ string text = value as string ?? value ? . ToString ( ) ?? string . Empty ;
531+ if ( outputDirectory != null )
532+ {
533+ string fileName = WholeProjectDecompiler . SanitizeFileName ( Path . GetFileName ( resourceName ) ) ;
534+ string fullPath = Path . Combine ( outputDirectory , fileName ) ;
535+ File . WriteAllText ( fullPath , text ) ;
536+ }
537+ else
538+ {
539+ output . Write ( text ) ;
540+ }
541+ }
542+ return 0 ;
543+ }
544+
433545 int ShowIL ( string assemblyFileName , TextWriter output )
434546 {
435547 var module = new PEFile ( assemblyFileName ) ;
@@ -450,7 +562,21 @@ ProjectId DecompileAsProject(string assemblyFileName, string projectFileName)
450562 {
451563 resolver . AddSearchDirectory ( path ) ;
452564 }
453- var decompiler = new WholeProjectDecompiler ( GetSettings ( module ) , resolver , null , resolver , TryLoadPDB ( module ) ) ;
565+ var settings = GetSettings ( module ) ;
566+ var debugInfo = TryLoadPDB ( module ) ;
567+ WholeProjectDecompiler decompiler ;
568+ if ( DecompileBamlFlag )
569+ {
570+ var bamlTypeSystem = new BamlDecompilerTypeSystem ( module , resolver ) ;
571+ var bamlSettings = new BamlDecompilerSettings {
572+ ThrowOnAssemblyResolveErrors = settings . ThrowOnAssemblyResolveErrors
573+ } ;
574+ decompiler = new BamlAwareWholeProjectDecompiler ( settings , resolver , resolver , debugInfo , bamlTypeSystem , bamlSettings ) ;
575+ }
576+ else
577+ {
578+ decompiler = new WholeProjectDecompiler ( settings , resolver , null , resolver , debugInfo ) ;
579+ }
454580 using ( var projectFileWriter = new StreamWriter ( File . OpenWrite ( projectFileName ) ) )
455581 return decompiler . DecompileProject ( module , Path . GetDirectoryName ( projectFileName ) , projectFileWriter ) ;
456582 }
0 commit comments