el Guille, la Web del Visual Basic, C#, .NET y más...

Compilar código en .NET Core (.NET 5.0) para consola y Windows Forms

 
Publicado el 08/Sep/2020
Actualizado el 08/Sep/2020
Autor: Guillermo 'guille' Som

Usando la aplicación que te mostré el otro día (Compilar y ejecutar versión para .NET Core (.NET 5.0)) y después de hacer varias pruebas con otra DLL que compila el código y lo ejecuta sin usar Process.Start, al menos si es una aplicación de consola, (ya que para ejecutar las de Windows Forms, al menos hasta lo que yo he probado, solo puedo lanzarla usando Process.Start); al final me he quedado con la llamada a dotnet para crear el proyecto, compilarlo y ejecutar el código compilado. (publicado en mi blog)




 

Este contenido está obtenido de mi blog, la URL original es:
Compilar código en .NET Core (.NET 5.0) para consola y Windows Forms

Pues eso… usando la aplicación que te mostré el otro día (Compilar y ejecutar versión para .NET Core (.NET 5.0)) y después de hacer varias pruebas con otra DLL que compila el código y lo ejecuta sin usar Process.Start, al menos si es una aplicación de consola, (ya que para ejecutar las de Windows Forms, al menos hasta lo que yo he probado, solo puedo lanzarla usando Process.Start); al final me he quedado con la llamada a dotnet para crear el proyecto, compilarlo y ejecutar el código compilado.

Nota:
Abajo te dejo un ZIP con el código fuente del proyecto (Compilar y ejecutar) y la utilidad para colorear (gsColorearCore).
En la carpeta pruebas del proyecto está el código fuente (de VB y C#) de la aplicación de ejemplo.

Compilar con dotnet (desde la línea de comandos)

En las primeras pruebas (las del código que te puse en el post arriba mencionado, usaba aplicaciones de consola, más que nada porque así se lo decía al usar el comando:

dotnet new console -o "AppDir" -lang c#|vb

Ese código lo que hace es crear una aplicación de consola en el directorio indicado por AppDir (si ya existe, podemos añadir –force al final para que genere el contenido aunque sobreesciba lo que ya hubiese.).

Para crear/compilar una aplicación para Windows Forms tendremos que usar una línea de comandos parecida a la anterior, pero indicando winforms en vez de console.
En el siguiente código creamos una aplicación de .NET 5.0 Core usando Visual Basic en el directorio E:\Guille\source\repos\MiAppWinF.

Nota:
Si no tienes instalado el .NET 5.0 y tienes al menos el .NET Core 3.0 o 3.1 simplemente usa el lenguaje C# en lugar de Visual Basic, ya que en esas versiones anteriores al 5.0 solo se permiten aplicaciones de Windows Forms o WPF para C#.

dotnet new winforms -o "E:\Guille\source\repos\MiAppWinF" -lang vb

Nota:
En realidad poner el directorio entre comillas dobles no es necesario, salvo que se utilice el comando dotnet build.

Una vez hecho esto, solo tendremos que modificar el código generado y si queremos que se ejecute el contenido de ese directorio podemos usa la siguiente línea de comandos:

dotnet run -p E:\Guille\source\repos\MiAppWinF

El código usado en la aplicación Compilar y ejecutar NETCore

En el código de la aplicación compilar y ejecutar para .NET Core 5.0 este último paso me lo salto y lo que hago es crear la aplicación (con –force) sustituyo el código. ya que –force o simplemente al crear con new se generan los ficheros en blanco (sin el código que ya tuviera).
En su lugar utilizo build para compilar el código.
Es decir, creo la aplicación con new (uso –force por si ya existiera), guardo el código fuente del editor del programa, lo compilo con build y finalmente lo ejecuto usando Process.Start.

El comando build usado es el siguiente:

dotnet build "E:\Guille\source\repos\MiAppWinF"

Las comillas dobles solo son necesarias si el path o el nombre del proyecto contiene espacios.

Y ese comando habrá creado un ejecutable (aparte de la DLL que siempre genera .NET Core) en el directorio: E:\Guille\source\repos\MiAppWinF\bin\Debug\net5.0-windows cuyo nombre será MiAppWinF.exe
Fíjate que al ser una aplicación para Windows (las aplicaciones de Windows Forms y las de WPF solo se pueden usar en plataformas Windows, es decir, no se pueden usar ni en Linux ni en MacOS) se crea en un directorio aparte de las aplicaciones de consola, que sería en: bin\Debug\net5.0\

Nota:
El directorio de salida puede ser diferente si antes de build (y después de new) cambias la configuración del proyecto.

Un poco de código fuente por favor

El código del método compilar al que se le pasa el código a compilar/ejecutar.

''' <summary>
''' Compilar el código indicado en el parámetro.
''' 
''' Usando dotnet (.NET Core) se hará lo siguiente:
''' Definir un directorio para VB o C#: MiApp_VB o MiApp_CS
''' Usar el comando:
''' dotnet new console -o dir -lang C#|VB --force
''' Copiar en ese directorio el fichero como Program.vb o .cs
''' Usar el comando (y redirigir la salida):
''' dotnet run -p dir
''' </summary>
Private Sub compilar(texto As String, mostrarSoloSalida As Boolean)
    ' Compilar usando la línea de comandos
    Dim ext = If(optCS.IsChecked, "cs", "vb")

    Dim appDir = System.IO.Path.Combine(System.Environment.CurrentDirectory,
                                        $"MiApp_{ext}")

    ' crear un fichero temporal para compilar
    Dim ficProgram = Path.Combine(appDir, $"Program.{ext}")

    Dim dotnet = "dotnet"
    Dim args = "--version"
    txtSalida.Text = $"{dotnet} {args} = {ejecutar(dotnet, args, waitSecs:=5000)}"

    ' Considero que es aplicación de Windows Forms              (07/Sep/20)
    ' si contiene InitializeComponent
    Dim esWF = texto.IndexOf("InitializeComponent()") > -1
    If esWF Then
        args = $"new winforms -o ""{appDir}"" -lang {If(optCS.IsChecked, "c#", "vb")} --force"
    Else
        args = $"new console -o ""{appDir}"" -lang {If(optCS.IsChecked, "c#", "vb")} --force"
    End If

    If mostrarSoloSalida Then
        ejecutar(dotnet, args, waitSecs:=10000)
    Else
        txtSalida.Text &= $"{vbCrLf}{vbCrLf}{ejecutar(dotnet, args, waitSecs:=10000)}"
    End If

    ' Eliminar todos los ficheros del lenguaje
    ' (solo quedará el que se guarde)
    Dim files = Directory.GetFiles(appDir, $"*.{ext}")
    For i = 0 To files.Length - 1
        File.Delete(files(i))
    Next

    ' Guardar el código a compilar
    Using sw As New System.IO.StreamWriter(ficProgram,
                                           False,
                                           System.Text.Encoding.UTF8)
        sw.Write(texto)
    End Using

    args = $"build ""{appDir}"""

    If mostrarSoloSalida Then
        Dim res = ejecutar(dotnet, args, waitSecs:=10000)
        Dim hayErrorComp = False
        If optVB.IsChecked Then
            If res.Contains("error BC") Then
                hayErrorComp = True
            End If
        Else
            If res.Contains("error CS") Then
                hayErrorComp = True
            End If
        End If
        If hayErrorComp Then
            txtSalida.Text &= vbCrLf & res
            txtSalida.Text &= $"{vbCrLf}--- FIN DE LA COMPILACIÓN CON ERRORES ---"
            Return
        End If
        'txtSalida.Text &= $"{vbCrLf}{vbCrLf}{res}"
    Else
        txtSalida.Text &= $"{vbCrLf}{vbCrLf}{ejecutar(dotnet, args, waitSecs:=10000)}"
    End If

    Dim exe = System.IO.Path.Combine(appDir, $"bin\Debug\net5.0\MiApp_{ext}.exe")
    ' Si es aplicación de Windows Forms, usa otro directorio    (07/Sep/20)
    If esWF Then
        'net5.0-windows
        exe = System.IO.Path.Combine(appDir, $"bin\Debug\net5.0-windows\MiApp_{ext}.exe")
        txtSalida.Text &= $"{vbCrLf}{vbCrLf}Aplicación de Windows"
    End If
    txtSalida.Text &= $"{vbCrLf}{vbCrLf}{ejecutar(exe, esWinF:=esWF)}"

    txtSalida.Text &= $"{vbCrLf}--- FIN DE LA COMPILACIÓN ---"

    txtSalida.SelectionStart = txtSalida.Text.Length
    txtSalida.SelectionLength = 0
End Sub

Del método ejecutar tengo dos versiones, la primera es la que uso para llamar al dotnet y en el que redirijo la salida de la consola para capturarla y mostrarla en la caja de textos txtSalida.
Al primero le paso el nombre de la aplicación a ejecutar (dotnet) y los argumentos.
Al segundo le paso el nombre del ejecutable y si es o no aplicación de Windows, en cuyo caso se usa ShellExecute y el estilo de la ventana es normal, aparte de no usar Kill para que no se cierre la aplicación al poco de haberse abierto 🙂

Private Function ejecutar(exe As String, arg As String,
                          Optional conKill As Boolean = False,
                          Optional waitSecs As Integer = 10000) As String
    Dim res = ""
    Using p As New Process
        p.StartInfo.FileName = exe
        p.StartInfo.Arguments = arg

        ' Indicamos que queremos redirigir la salida
        p.StartInfo.RedirectStandardOutput = True
        ' Para redirigir la salida, UseShellExecute debe ser falso
        p.StartInfo.UseShellExecute = False

        p.StartInfo.CreateNoWindow = True

        Try
            ' Iniciamos el proceso
            p.Start()

            ' Esperar a que el proceso finalice
            '
            ' Esperamos waitSecs segundos para que le de tiempo a ejecutarse
            ' como mínimo esperar 2000 (o 5000 si se usa dotnet)
            If waitSecs < 2000 Then waitSecs = 2000
            p.WaitForExit(waitSecs)

            If conKill Then
                p.Kill()
            End If

            res = p.StandardOutput.ReadToEnd()
        Catch ex As Exception

            res = ex.Message
        End Try
    End Using

    Return res
End Function

''' <summary>
''' Ejecutar el código y mostrarlo en la ventana de salida.
''' </summary>
Private Function ejecutar(exe As String,
                          Optional waitSecs As Integer = 2000,
                          Optional esWinF As Boolean = False) As String
    Dim p As New Process

    p.StartInfo.FileName = exe

    If esWinF Then

        With p.StartInfo
            .UseShellExecute = True
            .WindowStyle = ProcessWindowStyle.Normal
        End With

        ' Iniciamos el proceso
        p.Start()

        ' Esperar a que el proceso finalice
        '
        ' Esperamos 2 segundos para que le de tiempo a ejecutarse
        ' Como mínimo 2 segundos
        If waitSecs < 2000 Then waitSecs = 2000
        p.WaitForExit(waitSecs)

        Return ""
    End If

    ' Indicamos que queremos redirigir la salida
    p.StartInfo.RedirectStandardOutput = True
    ' Para redirigir la salida, UseShellExecute debe ser falso
    p.StartInfo.UseShellExecute = False

    ' No usar esto
    ' salvo que se use p.Kill()
    p.StartInfo.CreateNoWindow = True

    ' Iniciamos el proceso
    p.Start()

    ' Esperar a que el proceso finalice
    '
    ' Esperamos 2 segundos para que le de tiempo a ejecutarse
    ' Como mínimo 2 segundos
    If waitSecs < 2000 Then waitSecs = 2000
    p.WaitForExit(waitSecs)
    Try
        p.Kill()
    Catch ex As Exception
        Debug.WriteLine(ex.Message)
    End Try

    ' Convertir la salida usando el código de página 437
    ' que es la usada en MS-DOS (línea de comandos)
    Dim res = p.StandardOutput.ReadToEnd()

    Return res
End Function

Recomendación para usar código de Windows Forms en la utilidad de Compilar y ejecutar

Por último, comentarte que si quieres compilar una app para Windows Forms desde la utilidad de compilar y ejecutar, el código de esa aplicación debe estar en un solo fichero, en el que te recomiendo que incluyas el método Main, la definición del diseñador de Windows Forms y el código que realmente quieres usar.
Esto es así porque el programa/utilidad no está preparado para usar múltiples ficheros.

Te muestro a continuación cómo sería ese código para Visual Basic y para C# y una captura de la salida realizada al compilar desde la utilidad.

Este es el código de Visual Basic del formulario de pruebas.

Imports System
Imports System.Windows.Forms

'
' El punto de entrada del programa
'

Friend Module Program

    <STAThread()>
    Friend Sub Main(args As String())
        Application.SetHighDpiMode(HighDpiMode.SystemAware)
        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(False)
        Application.Run(New Form1)
    End Sub

End Module

'
' El diseñador del formulario
'

<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class Form1
    Inherits System.Windows.Forms.Form

    'Form overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
        Me.TextBox1 = New System.Windows.Forms.TextBox()
        Me.Button1 = New System.Windows.Forms.Button()
        Me.Label1 = New System.Windows.Forms.Label()
        Me.Button2 = New System.Windows.Forms.Button()
        Me.SuspendLayout()
        '
        'TextBox1
        '
        Me.TextBox1.Location = New System.Drawing.Point(12, 12)
        Me.TextBox1.Name = "TextBox1"
        Me.TextBox1.Size = New System.Drawing.Size(182, 23)
        Me.TextBox1.TabIndex = 0
        '
        'Button1
        '
        Me.Button1.Location = New System.Drawing.Point(200, 11)
        Me.Button1.Name = "Button1"
        Me.Button1.Size = New System.Drawing.Size(75, 23)
        Me.Button1.TabIndex = 1
        Me.Button1.Text = "Button1"
        Me.Button1.UseVisualStyleBackColor = True
        '
        'Label1
        '
        Me.Label1.Location = New System.Drawing.Point(12, 38)
        Me.Label1.Name = "Label1"
        Me.Label1.Size = New System.Drawing.Size(263, 23)
        Me.Label1.TabIndex = 2
        Me.Label1.Text = "Label1"
        '
        'Button2
        '
        Me.Button2.Location = New System.Drawing.Point(200, 90)
        Me.Button2.Name = "Button2"
        Me.Button2.Size = New System.Drawing.Size(75, 23)
        Me.Button2.TabIndex = 3
        Me.Button2.Text = "Cerrar"
        Me.Button2.UseVisualStyleBackColor = True
        '
        'Form1
        '
        Me.AcceptButton = Me.Button1
        Me.AutoScaleDimensions = New System.Drawing.SizeF(7.0!, 15.0!)
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
        Me.CancelButton = Me.Button2
        Me.ClientSize = New System.Drawing.Size(287, 125)
        Me.Controls.Add(Me.Button2)
        Me.Controls.Add(Me.Label1)
        Me.Controls.Add(Me.Button1)
        Me.Controls.Add(Me.TextBox1)
        Me.Name = "Form1"
        Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen
        Me.Text = "Form1"
        Me.ResumeLayout(False)
        Me.PerformLayout()

    End Sub

    Friend WithEvents TextBox1 As TextBox
    Friend WithEvents Button1 As Button
    Friend Label1 As Label
    Friend Button2 As Button
End Class

'
' El código del formulario
'

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        TextBox1.Text = "<tu nombre>"

        'AddHandler Button2.Click, AddressOf Button2_Click
        AddHandler Button2.Click, Sub() Me.Close()
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim str = TextBox1.Text
        If str = "<tu nombre>" Then
            str = " amigo"
        End If
        Label1.Text = $"¡Hola {ToUpperFirst(str)}!"
    End Sub

    ''' <summary>
    ''' Convierte en mayúsculas el primer carácter de la cadena indicada.
    ''' </summary>
    Private Function ToUpperFirst(str As String) As String
        'Return str(0).ToString().ToUpper() & str.Substring(1)
        if str="" then str=" amigo"
        Return str(0).ToString.ToUpper & str.Substring(1)
    End Function

    'Private Sub Button2_Click(sender As Object, e As EventArgs)
    '    Me.Close()
    'End Sub
End Class

Este es el código de C# del formulario de pruebas.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;

//
// El punto de entrada del programa
//

static class Program
{
    /// <summary>
    ///  The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.SetHighDpiMode(HighDpiMode.SystemAware);
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}


//
// El diseñador del formulario
//

partial class Form1
{
    /// <summary>
    ///  Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    ///  Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    ///  Required method for Designer support - do not modify
    ///  the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.textBox1 = new System.Windows.Forms.TextBox();
        this.button1 = new System.Windows.Forms.Button();
        this.label1 = new System.Windows.Forms.Label();
        this.button2 = new System.Windows.Forms.Button();
        this.SuspendLayout();
        // 
        // textBox1
        // 
        this.textBox1.Location = new System.Drawing.Point(12, 12);
        this.textBox1.Name = "textBox1";
        this.textBox1.Size = new System.Drawing.Size(182, 23);
        this.textBox1.TabIndex = 0;
        // 
        // button1
        // 
        this.button1.Location = new System.Drawing.Point(200, 12);
        this.button1.Name = "button1";
        this.button1.Size = new System.Drawing.Size(75, 23);
        this.button1.TabIndex = 1;
        this.button1.Text = "button1";
        this.button1.UseVisualStyleBackColor = true;
        this.button1.Click += new System.EventHandler(this.button1_Click);
        // 
        // label1
        // 
        this.label1.Location = new System.Drawing.Point(12, 38);
        this.label1.Name = "label1";
        this.label1.Size = new System.Drawing.Size(263, 23);
        this.label1.TabIndex = 2;
        this.label1.Text = "label1";
        // 
        // button2
        // 
        this.button2.Location = new System.Drawing.Point(200, 90);
        this.button2.Name = "button2";
        this.button2.Size = new System.Drawing.Size(75, 23);
        this.button2.TabIndex = 3;
        this.button2.Text = "Cerrar";
        this.button2.UseVisualStyleBackColor = true;
        //this.button2.Click += new System.EventHandler(this.button1_Click);
        // 
        // Form1
        // 
        this.AcceptButton = this.button1;
        this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.CancelButton = this.button2;
        this.ClientSize = new System.Drawing.Size(287, 125);
        this.Controls.Add(this.label1);
        this.Controls.Add(this.button1);
        this.Controls.Add(this.textBox1);
        this.Controls.Add(this.button2);
        this.Name = "Form1";
        this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
        this.Text = "Form1";
        this.Load += new System.EventHandler(this.Form1_Load);
        this.ResumeLayout(false);
        this.PerformLayout();

    }

    #endregion

    private System.Windows.Forms.TextBox textBox1;
    private System.Windows.Forms.Button button1;
    private System.Windows.Forms.Label label1;
    private System.Windows.Forms.Button button2;
}

//
// El código del formulario
//

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        textBox1.Text = "<tu nombre>";
        
        //button2.Click += => (object o, EventArgs e) this.Close();
        //button2.Click += delegate (object sender, EventArgs e) { this.Close(); };
        button2.Click += (sender, e) => { this.Close(); };
    }

    private void button1_Click(object sender, EventArgs e)
    {
        //label1.Text = $"Hola {ToUpperFirst(textBox1.Text)}!";
        var str = textBox1.Text;
        if( str == "<tu nombre>" ) str = " amigo";
        label1.Text = $"Hola {ToUpperFirst(str)}!";
    }

    /// <summary>
    /// Convierte en mayúsculas el primer carácter de la cadena indicada.
    /// </summary>
    private string ToUpperFirst(string str)
    {
        if(str=="") str = " amigo";
        return str[0].ToString().ToUpper() + str.Substring(1);
    }
}

Esta es la salida del código ejecutándose con la utilidad de compilar y ejecutar para NETCore.

Figura 1. El resultado de compilar y ejecutar una aplicación de Windows Forms para .NET Core 5.0.

Y esto es todo… tengo algo más por ahí, para seguir con la compilación y ejecución del código para .NET Core, pero eso será para otra ocasión 🙂

 

Espero que te haya sido de utilidad… Esa es la idea y ¡eso espero! 🙂

 

Nos vemos.
Guillermo

El ZIP con el código fuente:

ZIP: Compilar_ejecutar_NetCore_20200908_1551.zip (71.8 KB)
MD5 checksum: 52A9AD9A1F1605547D1C8451CADD4CF0



 


La fecha/hora en el servidor es: 14/10/2024 11:18:25

La fecha actual GMT (UTC) es: 

©Guillermo 'guille' Som, 1996-2024