0

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

ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
  • without an entry point/Sub Main, it is not an executable and wont run. If you want `MainClass.MainMenthod` to execute, there has to be code somewhere to instance the class and invoke the method. Maybe you want a DLL? – Ňɏssa Pøngjǣrdenlarp Jun 14 '15 at 12:11
  • @Plutonix No, I pretend to compile an executable, but just not a commandline executable. I want to compile an exe that does not open the console, just like a WinForms project. Do you understand what I pretend to do?, an exe that don't have console but has a main method, or in other words I want a GUI app that will have set an "nvisible form, but I don't know how to create that sourcecode at execution time like the sourcecodes of the examples above because the codedomprovider seems it only accepts a modules (of a commandline app) not forms, I can't explain it in a better way. Thanks for comment – ElektroStudios Jun 14 '15 at 12:31
  • Such an app still requires a Sub Main - The entry point. All apps (executables) have them, they are hidden in VB apps that use a Main Startup form but it is there (`Application.Run(New MainForm)`). – Ňɏssa Pøngjǣrdenlarp Jun 14 '15 at 12:36
  • @Plutonix Thanks but the problem is that it stills a commandline app. A WinForms project does not show a console, then I just don't want to show a console when this exe is ran (I don't want to call APIs to hide the console after shown or tricks like that, I just want to avoid the console instance like when compiling a WinForms project), I'm using this code: http://pastebin.com/DCuX4quL – ElektroStudios Jun 14 '15 at 12:44
  • I found the solution, was very easy!! I only need to add this parameter to the compiler parameters: `/target:winexe` as explained here: http://stackoverflow.com/questions/2661171/hiding-command-prompt-with-codedomprovider – ElektroStudios Jun 14 '15 at 12:51

1 Answers1

1

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
ElektroStudios
  • 19,105
  • 33
  • 200
  • 417