Compile a non-commandline application using CodeDomProvider?

290 views Asked by At

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?.

1

There are 1 answers

0
ElektroStudios On BEST ANSWER

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