.netvb.netwinformscodedomsystem-codedom-compiler

Compile a non-commandline application using CodeDomProvider?


I've written this simple function to automate the compilation of a single exe assembly with an embedded resource:

Public Shared Function CompileAssembly(ByVal codeProvider As CodeDomProvider,
                                   ByVal isExecutable As Boolean,
                                   ByVal targetFile As String,
                                   Optional ByVal resources As IEnumerable(Of String) = Nothing,
                                   Optional ByVal code As String = "") As CompilerResults

Dim cp As New CompilerParameters
With cp

    ' Generate an exe or a dll.
    .GenerateExecutable = isExecutable

    ' Set the assembly file name to generate.
    .OutputAssembly = targetFile

    ' Set compiler argument to optimize output.
    .CompilerOptions = "/optimize"

    ' Specify the class that contains the main method of the executable.
    If codeProvider.Supports(GeneratorSupport.EntryPointMethod) Then
        .MainClass = "MainClass"
    End If

    ' Set the embedded resource file of the assembly. 
    If codeProvider.Supports(GeneratorSupport.Resources) AndAlso resources IsNot Nothing Then
        .EmbeddedResources.AddRange(resources.ToArray)
    End If

End With

Return codeProvider.CompileAssemblyFromSource(cp, code)

End Function

The problem is that I need to compile a non-commandline application, a Class like this which I provide a method that should be executed when the app runs:

    Dim sourceCode As String =
        <a>
Public Class MainClass 

    Sub MainMethod()
       ' Do Something when the app is executed...
    End Sub

End Class
        </a>.Value

but I can't find the way to do it.

I only can compile console applications because the codeprovider seems that needs an entrypoint, so I only be able to compile this, which is not what I want:

    Dim Sourcecode As String =
        <a>
Module MainModule

    Sub Main()
    End Sub

End Module
        </a>.Value

How I could do this for my needs?.


Solution

  • I just want to share my working CodeDomProvider compiler routine:

    Imports System.CodeDom.Compiler
    
    Namespace Tools
    
        Public NotInheritable Class CodeDomUtil
    
            ''' <summary>
            ''' Specifies a <see cref="CompilerParameters"></see> target assembly.
            ''' </summary>
            Public Enum TargetAssembly As Integer
    
                ''' <summary>
                ''' A Command line interface executable.
                ''' </summary>
                Cli = 0
    
                ''' <summary>
                ''' A Graphical user interface executable.
                ''' </summary>
                Gui = 1
    
                ''' <summary>
                ''' A Dynamic-link library.
                ''' </summary>
                Dll = 2
    
            End Enum
    
            ''' <remarks>
            ''' *****************************************************************
            ''' Title : Compile Assembly (from reaource).
            ''' Author: Elektro
            ''' Date  : 14-June-2015
            ''' Usage : 
            ''' 
            ''' Using vbCodeProvider As New Microsoft.VisualBasic.VBCodeProvider
            ''' 
            '''     Dim resultVB As CompilerResults =
            '''         CodeDomUtil.CompileAssembly(codeProvider:=vbCodeProvider,
            '''                                     targetAssembly:=CodeDomUtil.TargetAssembly.Dll,
            '''                                     targetFile:="C:\VB Assembly.dll",
            '''                                     resources:={"C:\MyResources.resx"},
            '''                                     referencedAssemblies:={"System.dll"},
            '''                                     mainClassName:="MainNamespace.MainClass",
            '''                                     sourceCode:=<a>
            '''                                                 Imports System
            ''' 
            '''                                                 Namespace MainNamespace
            ''' 
            '''                                                     Public NotInheritable MainClass
            ''' 
            '''                                                     End Class
            ''' 
            '''                                                 End Namespace
            '''                                                 </a>.Value)
            ''' 
            '''     Dim warnings As IEnumerable(Of CompilerError) =
            '''         From ce As CompilerError In resultVB.Errors.Cast(Of CompilerError)()
            '''         Where ce.IsWarning
            ''' 
            '''     Dim errors As IEnumerable(Of CompilerError) =
            '''         From ce As CompilerError In resultVB.Errors.Cast(Of CompilerError)()
            '''         Where Not ce.IsWarning
            ''' 
            '''     For Each war As CompilerError In warnings
            '''         Debug.WriteLine(String.Format("{0}| Warning: {1}", war.ErrorNumber, war.ErrorText))
            '''     Next war
            ''' 
            '''     For Each err As CompilerError In errors
            '''         Debug.WriteLine(String.Format("{0}| Error: {1}", err.ErrorNumber, err.ErrorText))
            '''     Next err
            ''' 
            ''' End Using
            ''' -----------------------------------------------------------------
            ''' Using csCodeProvider As New Microsoft.CSharp.CSharpCodeProvider
            '''
            '''     Dim resultCS As CompilerResults =
            '''         CodeDomUtil.CompileAssembly(codeProvider:=csCodeProvider,
            '''                                     targetAssembly:=CodeDomUtil.TargetAssembly.Dll,
            '''                                     targetFile:="C:\C# Assembly.dll",
            '''                                     resources:={"C:\MyResources.resx"},
            '''                                     referencedAssemblies:={"System.dll"},
            '''                                     mainClassName:="MainNamespace.MainClass",
            '''                                     sourceCode:=<a>
            '''                                                 using System;
            '''
            '''                                                 namespace MainNamespace
            '''                                                 {
            '''                                                     class MainClass
            '''                                                     {
            '''
            '''                                                     }
            '''                                                 }
            '''                                                 </a>.Value)
            '''
            '''     Dim warnings As IEnumerable(Of CompilerError) =
            '''         From ce As CompilerError In resultCS.Errors.Cast(Of CompilerError)()
            '''         Where ce.IsWarning
            '''
            '''     Dim errors As IEnumerable(Of CompilerError) =
            '''         From ce As CompilerError In resultCS.Errors.Cast(Of CompilerError)()
            '''         Where Not ce.IsWarning
            '''
            '''     For Each war As CompilerError In warnings
            '''         Debug.WriteLine(String.Format("{0}| Warning: {1}", war.ErrorNumber, war.ErrorText))
            '''     Next war
            '''
            '''     For Each err As CompilerError In errors
            '''         Debug.WriteLine(String.Format("{0}| Error: {1}", err.ErrorNumber, err.ErrorText))
            '''     Next err
            '''
            ''' End Using
            ''' *****************************************************************
            ''' </remarks>
            ''' <summary>
            ''' Compiles a .Net assembly as executable or link library.
            ''' </summary>
            ''' <param name="codeProvider">The code provider.</param>
            ''' <param name="targetAssembly">The kind of assembly to generate.</param>
            ''' <param name="targetFile">The target file to create.</param>
            ''' <param name="resources">The embedded resources (if any).</param>
            ''' <param name="referencedAssemblies">The referenced assemblies (if any).</param>
            ''' <param name="mainClassName">The code to compile (if any).</param>
            ''' <param name="sourceCode">The sourcecode to compile (if any).</param>
            ''' <exception cref="Exception">The current CodeDomProvider does not support resource embedding.</exception>
            ''' <exception cref="NotImplementedException">Default sourcecode is not implemented for the specified CodeDomProvider. Please, set a sourcecode yourself.</exception>
            ''' <returns>The results of the compiler operation.</returns>
            Public Shared Function CompileAssembly(ByVal codeProvider As CodeDomProvider,
                                                   ByVal targetAssembly As TargetAssembly,
                                                   ByVal targetFile As String,
                                                   Optional ByVal resources As IEnumerable(Of String) = Nothing,
                                                   Optional ByVal referencedAssemblies As IEnumerable(Of String) = Nothing,
                                                   Optional ByVal mainClassName As String = "MainNamespace.MainClass",
                                                   Optional ByVal sourceCode As String = Nothing) As CompilerResults
    
                ' Set a default assembly reference.
                If referencedAssemblies Is Nothing Then
                    referencedAssemblies = {"System.dll"}
                End If
    
                Dim cp As New CompilerParameters
                With cp
    
                    ' Set compiler arguments.
                    Select Case targetAssembly
    
                        Case CodeDomUtil.TargetAssembly.Gui
                            .CompilerOptions = "/optimize /target:winexe"
    
                        Case Else
                            .CompilerOptions = "/optimize"
    
                    End Select
    
                    ' Generate an exe or a dll.
                    .GenerateExecutable = (targetAssembly <> CodeDomUtil.TargetAssembly.Dll)
    
                    ' Save the assembly as a physical file.
                    .GenerateInMemory = False
    
                    ' Generate debug information (pdb).
                    .IncludeDebugInformation = False
    
                    ' Set the assembly file name to generate.
                    .OutputAssembly = targetFile
    
                    ' Add an assembly reference.
                    .ReferencedAssemblies.AddRange(referencedAssemblies.ToArray)
    
                    ' Set a temporary files collection. 
                    ' The TempFileCollection stores the temporary files generated during a build in the current directory.
                    .TempFiles = New TempFileCollection(tempdir:=IO.Path.GetTempPath(), keepFiles:=True)
    
                    ' Set whether to treat all warnings as errors.
                    .TreatWarningsAsErrors = False
    
                    ' Set the level at which the compiler should start displaying warnings.
                    ' 0 - Turns off emission of all warning messages.
                    ' 1 - Displays severe warning messages.
                    ' 2 - Displays level 1 warnings plus certain, less-severe warnings, such as warnings about hiding class members.
                    ' 3 - Displays level 2 warnings plus certain, less-severe warnings, such as warnings about expressions that always evaluate to true or false.
                    ' 4 - Displays all level 3 warnings plus informational warnings. This is the default warning level at the command line.
                    .WarningLevel = 3
    
                    ' Set the embedded resource file of the assembly. 
                    If codeProvider.Supports(GeneratorSupport.Resources) AndAlso (resources IsNot Nothing) Then
                        .EmbeddedResources.AddRange(resources.ToArray)
    
                    ElseIf (Not codeProvider.Supports(GeneratorSupport.Resources)) AndAlso (resources IsNot Nothing) Then
                        Throw New Exception(message:="The current CodeDomProvider does not support resource embedding.")
    
                    End If
    
                    ' Specify the class that contains the main method of the executable.
                    If codeProvider.Supports(GeneratorSupport.EntryPointMethod) Then
    
                        .MainClass = mainClassName
    
                        If (TypeOf codeProvider Is Microsoft.VisualBasic.VBCodeProvider) AndAlso
                           (String.IsNullOrEmpty(sourceCode)) AndAlso
                           .GenerateExecutable Then
    
                            sourceCode =
                                <a>
                                Imports System
    
                                Namespace MainNamespace
    
                                    Module MainClass
    
                                        Sub Main()
                                        End Sub
    
                                    End Module
    
                                End Namespace
                                </a>.Value
    
                        ElseIf (TypeOf codeProvider Is Microsoft.VisualBasic.VBCodeProvider) AndAlso
                               (String.IsNullOrEmpty(sourceCode)) AndAlso
                               Not .GenerateExecutable Then
    
                            sourceCode =
                                <a>
                                Imports System
    
                                Namespace MainNamespace
    
                                    Public NotInheritable MainClass
    
                                    End Class
    
                                End Namespace
                                </a>.Value
    
                        ElseIf (TypeOf codeProvider Is Microsoft.CSharp.CSharpCodeProvider) AndAlso
                               (String.IsNullOrEmpty(sourceCode)) AndAlso
                              .GenerateExecutable Then
    
                            sourceCode =
                                <a>
                                using System;
    
                                namespace MainNamespace
                                {
                                    class MainClass
                                    {
                                        static void Main(string[] args)
                                        {
    
                                        }
                                    }
                                }
                                </a>.Value
    
                        ElseIf (TypeOf codeProvider Is Microsoft.CSharp.CSharpCodeProvider) AndAlso
                               (String.IsNullOrEmpty(sourceCode)) AndAlso
                               Not .GenerateExecutable Then
    
                            sourceCode =
                                <a>
                                using System;
    
                                namespace MainNamespace
                                {
                                    class MainClass
                                    {
    
                                    }
                                }
                                </a>.Value
    
                        ElseIf String.IsNullOrEmpty(sourceCode) Then
                            Throw New NotImplementedException(message:="Default sourcecode is not implemented for the specified CodeDomProvider. Please, specify a sourcecode.")
    
                        End If
    
                    End If
    
                End With
    
                Return codeProvider.CompileAssemblyFromSource(cp, sourceCode)
    
            End Function
    
            ''' <remarks>
            ''' *****************************************************************
            ''' Title : Compile Assembly (from file).
            ''' Author: Elektro
            ''' Date  : 14-June-2015
            ''' Usage : 
            ''' 
            ''' Using vbCodeProvider As New Microsoft.VisualBasic.VBCodeProvider
            '''
            '''     Dim resultVB As CompilerResults =
            '''         CodeDomUtil.CompileAssembly(codeProvider:=vbCodeProvider,
            '''                                     targetAssembly:=CodeDomUtil.TargetAssembly.Dll,
            '''                                     sourceFile:="C:\SourceCode.vb",
            '''                                     targetFile:="C:\VB Assembly.dll",
            '''                                     resources:={"C:\MyResources.resx"},
            '''                                     referencedAssemblies:={"System.dll"},
            '''                                     mainClassName:="MainNamespace.MainClass")
            '''
            '''     Dim warnings As IEnumerable(Of CompilerError) =
            '''         From ce As CompilerError In resultVB.Errors.Cast(Of CompilerError)()
            '''         Where ce.IsWarning
            '''
            '''     Dim errors As IEnumerable(Of CompilerError) =
            '''         From ce As CompilerError In resultVB.Errors.Cast(Of CompilerError)()
            '''         Where Not ce.IsWarning
            '''
            '''     For Each war As CompilerError In warnings
            '''         Debug.WriteLine(String.Format("{0}| Warning: {1}", war.ErrorNumber, war.ErrorText))
            '''     Next war
            '''
            '''     For Each err As CompilerError In errors
            '''         Debug.WriteLine(String.Format("{0}| Error: {1}", err.ErrorNumber, err.ErrorText))
            '''     Next err
            '''
            ''' End Using
            ''' -----------------------------------------------------------------
            ''' Using csCodeProvider As New Microsoft.CSharp.CSharpCodeProvider
            '''
            '''     Dim resultCS As CompilerResults =
            '''         CodeDomUtil.CompileAssembly(codeProvider:=csCodeProvider,
            '''                                     targetAssembly:=CodeDomUtil.TargetAssembly.Dll,
            '''                                     sourceFile:="C:\SourceCode.cs",
            '''                                     targetFile:="C:\CS Assembly.dll",
            '''                                     resources:={"C:\MyResources.resx"},
            '''                                     referencedAssemblies:={"System.dll"},
            '''                                     mainClassName:="MainNamespace.MainClass")
            '''
            '''     Dim warnings As IEnumerable(Of CompilerError) =
            '''         From ce As CompilerError In resultCS.Errors.Cast(Of CompilerError)()
            '''         Where ce.IsWarning
            '''
            '''     Dim errors As IEnumerable(Of CompilerError) =
            '''         From ce As CompilerError In resultCS.Errors.Cast(Of CompilerError)()
            '''         Where Not ce.IsWarning
            '''
            '''     For Each war As CompilerError In warnings
            '''         Debug.WriteLine(String.Format("{0}| Warning: {1}", war.ErrorNumber, war.ErrorText))
            '''     Next war
            '''
            '''     For Each err As CompilerError In errors
            '''         Debug.WriteLine(String.Format("{0}| Error: {1}", err.ErrorNumber, err.ErrorText))
            '''     Next err
            '''
            ''' End Using
            ''' *****************************************************************
            ''' </remarks>
            ''' <summary>
            ''' Compiles a .Net assembly as executable or link library.
            ''' </summary>
            ''' <param name="codeProvider">The code provider.</param>
            ''' <param name="targetAssembly">The kind of assembly to generate.</param>
            ''' <param name="sourceFile">The source file to compile.</param>
            ''' <param name="targetFile">The target file to create.</param>
            ''' <param name="resources">The embedded resources (if any).</param>
            ''' <param name="referencedAssemblies">The referenced assemblies (if any).</param>
            ''' <param name="mainClassName">The code to compile (if any).</param>
            ''' <exception cref="Exception">The current CodeDomProvider does not support resource embedding.</exception>
            ''' <returns>The results of the compiler operation.</returns>
            Public Shared Function CompileAssembly(ByVal codeProvider As CodeDomProvider,
                                                   ByVal targetAssembly As TargetAssembly,
                                                   ByVal sourceFile As String,
                                                   ByVal targetFile As String,
                                                   Optional ByVal resources As IEnumerable(Of String) = Nothing,
                                                   Optional ByVal referencedAssemblies As IEnumerable(Of String) = Nothing,
                                                   Optional ByVal mainClassName As String = "MainNamespace.MainClass") As CompilerResults
    
                ' Set a default assembly reference.
                If referencedAssemblies Is Nothing Then
                    referencedAssemblies = {"System.dll"}
                End If
    
                Dim cp As New CompilerParameters
                With cp
    
                    ' Set compiler arguments.
                    Select Case targetAssembly
    
                        Case CodeDomUtil.TargetAssembly.Gui
                            .CompilerOptions = "/optimize /target:winexe"
    
                        Case Else
                            .CompilerOptions = "/optimize"
    
                    End Select
    
                    ' Generate an exe or a dll.
                    .GenerateExecutable = (targetAssembly <> CodeDomUtil.TargetAssembly.Dll)
    
                    ' Save the assembly as a physical file.
                    .GenerateInMemory = False
    
                    ' Generate debug information (pdb).
                    .IncludeDebugInformation = False
    
                    ' Set the assembly file name to generate.
                    .OutputAssembly = targetFile
    
                    ' Add an assembly reference.
                    .ReferencedAssemblies.AddRange(referencedAssemblies.ToArray)
    
                    ' Set a temporary files collection. 
                    ' The TempFileCollection stores the temporary files generated during a build in the current directory.
                    .TempFiles = New TempFileCollection(tempdir:=IO.Path.GetTempPath(), keepFiles:=True)
    
                    ' Set whether to treat all warnings as errors.
                    .TreatWarningsAsErrors = False
    
                    ' Set the level at which the compiler should start displaying warnings.
                    ' 0 - Turns off emission of all warning messages.
                    ' 1 - Displays severe warning messages.
                    ' 2 - Displays level 1 warnings plus certain, less-severe warnings, such as warnings about hiding class members.
                    ' 3 - Displays level 2 warnings plus certain, less-severe warnings, such as warnings about expressions that always evaluate to true or false.
                    ' 4 - Displays all level 3 warnings plus informational warnings. This is the default warning level at the command line.
                    .WarningLevel = 3
    
                    ' Set the embedded resource file of the assembly. 
                    If codeProvider.Supports(GeneratorSupport.Resources) AndAlso (resources IsNot Nothing) Then
                        .EmbeddedResources.AddRange(resources.ToArray)
    
                    ElseIf (Not codeProvider.Supports(GeneratorSupport.Resources)) AndAlso (resources IsNot Nothing) Then
                        Throw New Exception(message:="The current CodeDomProvider does not support resource embedding.")
    
                    End If
    
                    ' Specify the class that contains the main method of the executable.
                    If codeProvider.Supports(GeneratorSupport.EntryPointMethod) Then
                        .MainClass = mainClassName
                    End If
    
                End With
    
                Return codeProvider.CompileAssemblyFromFile(cp, {sourceFile})
    
            End Function
    
        End Class
    
    End Namespace