VB.NET: avviare un’applicazione all’interno di un form (Windows Form)

La tecnologia Windows Form è tutt’altro che morta: qualcuno mi chiede ancora informazioni su “come si fa questo e come si fa quello”. In questo articolo, quindi, voglio affrontare il problema dell’avvio di applicazioni esterne (eseguibili) in tre distinti contesti:

1. L’avvio puro e semplice di un eseguibile esterno;

2. L’avvio di un eseguibile esterno all’interno di un form VB.NET;

3. Il posizionamento della finestra dell’eseguibile esterno.

PRIMA DI INIZIARE

Create un’applicazione Windows Form, nel linguaggio Visual Basic e inserite un pulsante nel form per poter eseguire il codice che vedremo in seguito.

Nelle prime righe del codice, prima ancora della definizione del form, inserite le seguenti due istruzioni, perché ci serviranno in seguito.

Imports System.Diagnostics

Imports System.Runtime.InteropServices

AVVIO DI UN ESEGUIBILE ESTERNO

Per semplicità, immaginiamo di voler far partire un’istanza di NOTEPAD.EXE. I concetti che vedremo valgono, comunque, per quasi tutti i tipi di applicazioni.

Per avviare un’applicazione esterna si utilizza il metodo Process.Start, come segue:

Process.Start(“NOTEPAD.EXE”)

Inseriamo questa istruzione nel metodo gestore dell’evento Click del pulsante.

Dopo aver avviato l’applicazione, clicchiamo sul pulsante e osserviamo cosa succede: l’applicazione NotePad sarà avviata, ma in modo indipendente rispetto all’applicazione Windows Form (che a sua volta sarà visibile solamente con il form contenente il solo pulsante).

In qualche caso può essere la modalità preferita di avvio delle applicazioni, per esempio se vogliamo creare un’applicazione con una serie di “pulsanti-scorciatoia” per avviare alcune applicazioni di varia utilità.

In molti casi, la modalità precedente non è opportuna, perché causa disordine e fa pensare a un’applicazione non ben congegnata. Una modalità migliore è quella che visualizza le applicazioni esterne all’interno del form stesso.

AVVIO DI FORM “FIGLI” E APPLICAZIONI ESTERNE ALL’INTERNO DEL FORM

Prima di vedere cosa succede con le applicazioni esterne (eseguibili), vediamo come si crea un normale form “figlio” nel form principale (che deve essere impostato come form MDI) e come si posiziona.

‘ imposto il form principale come form MDI:

Me.IsMdiContainer = True

‘ creo un form secondario

‘ Nota: in questo esempio creiamo un form “al volo”, ma in

‘ genere si crea un form dalla finestra Esplora Soluzioni

Dim f2 As New Form

‘ il form f2 viene impostato come form “figlio” del principale (Me)

f2.MdiParent = Me

‘ visualizzo il form secondario f2 (vuoto):

f2.Show()

‘ nel seguente ciclo, seleziono ciascun form “figlio”

‘ (Me.MdiChildren fornisce un array dei form figli)

‘ e lo sposto nella posizione (x=10,y=10):

For Each f As Form In Me.MdiChildren

   ‘ l’impostazione della proprietà StartPosition al valore

   ‘ FormStartPosition.Manual è importante, perché altrimenti

   ‘ non vedrete il form nella posizione desiderata:

   f.StartPosition = FormStartPosition.Manual

   ‘ nel seguente ciclo, seleziono ciascun form “figlio”

   f.Location = New Point(10, 10)

Next

Ovviamente nel nostro esempio avevamo un unico form figlio e quindi il ciclo verrà eseguito una volta sola, ma nell’ipotesi di avere due o più form figli, vedremo che ciascun form si disporrà nella posizione indicata.

AVVIO DI APPLICAZIONI ESTERNE ALL’INTERNO DEL FORM

Quello che abbiamo visto per i form “figli” (form “nativi” del .NET Framework) non si può applicare alle applicazioni esterne, cioè agli eseguibili. Per poter visualizzare un’applicazione esterna all’interno del nostro form, infatti, dobbiamo utilizzare una API di Windows che si chiama SetParent.

Vediamo come si dichiara questa API in VB.NET:

<DllImport(“user32.dll”, SetLastError:=True, CharSet:=CharSet.Auto)> _

    Public Shared Function SetParent(ByVal hWndChild As IntPtr, _

    ByVal hWndNewParent As IntPtr) As IntPtr

End Function

Una volta dichiarata, possiamo utilizzare la funzione SetParent come se fosse un’istruzione VB.

Il codice di esempio è il seguente:

Imports System.Diagnostics

Imports System.Runtime.InteropServices

Public Class Form1

    ‘ dichiarazione della funzione SetParent:

    <DllImport(“user32.dll”, SetLastError:=True,

        CharSet:=CharSet.Auto)> _

    Public Shared Function SetParent(

        ByVal hWndChild As IntPtr, _

        ByVal hWndNewParent As IntPtr) As IntPtr

    End Function

    Private Sub Button1_Click(

            sender As Object, e As EventArgs) _

            Handles Button1.Click

        ‘ imposto il form principale come form MDI:

        Me.IsMdiContainer = True

        ‘ definisco il processo da avviare:

        Dim pi As New ProcessStartInfo(“NOTEPAD.EXE”)

        ‘ avvio il processo:

        Dim p As Process = Process.Start(pi)

        ‘ permetto al processo di avviarsi correttamente e

        ‘ completamente. Se non lo facessi, potrei incorrere

        ‘ in un errore, se il processo è lento:

        Do While p.MainWindowHandle.Equals(IntPtr.Zero)

            Threading.Thread.Sleep(50)

            p.Refresh()

        Loop

        ‘ imposto l’handle della finestra del processo

        ‘ appena avviato come figlio dell’handle della

        ‘ finestra principale dell’applicazione

        SetParent(p.MainWindowHandle, Me.Handle)

    End Sub

End Class

Avviando l’applicazione e cliccando sul pulsante di avvio, vedremo che l’applicazione esterna sarà effettivamente avviata all’interno della nostra applicazione VB.NET.

Purtroppo l’applicazione appare troppo grande e posizionata in modo errato rispetto alla finestra dell’applicazione VB.NET, con un risultato estetico decisamente insoddisfacente.

Per posizionare correttamente l’applicazione “figlia” potremmo pensare di agire nello stesso modo in cui abbiamo spostato e ridimensionato il form “figlio” nel primo esempio. In questo caso, però, la tecnica non funziona, perché non possiamo agire sulle proprietà dell’applicazione esterna (non abbiamo le proprietà StartPosition e Location da poter modificare).

Non resta che fare ricorso ancora una volta alle API di Windows: vediamo nel paragrafo successivo come si fa.

SPOSTAMENTO DELLA FINESTRA DELL’ESEGUIBILE ESTERNO

Per spostare una finestra di un’applicazione in esecuzione, possiamo utilizzare la funzione MoveWindow, inclusa nelle API di Windows. La definizione della funzione è la seguente:

<DllImport(“user32.dll”, SetLastError:=True,

    CharSet:=CharSet.Auto)> _

Public Shared Function MoveWindow(

    ByVal hWnd As IntPtr,

    ByVal x As Integer, ByVal y As Integer,

    ByVal nWidth As Integer,

    ByVal nHeight As Integer,

    ByVal bRepaint As Boolean) As Boolean

End Function

Posizioniamo e ridimensioniamo, quindi, la finestra di “NOTEPAD.EXE” con i seguenti parametri:

– posizione “X”: 10

– posizione “Y”: 5

– larghezza: 600

– altezza: 500

Nel codice dell’esempio precedentemente visto dobbiamo aggiungere solamente la dichiarazione della funzione MoveWindow e l’istruzione che esegue effettivamente lo spostamento e il ridimensionamento, cioè questa istruzione:

MoveWindow(p.MainWindowHandle, 10, 5, 600, 500, True)

Vediamo quindi il codice completo:

Imports System.Diagnostics

Imports System.Runtime.InteropServices

Public Class Form1

    <DllImport(“user32.dll”, SetLastError:=True,

        CharSet:=CharSet.Auto)> _

    Public Shared Function SetParent(

        ByVal hWndChild As IntPtr, _

        ByVal hWndNewParent As IntPtr) As IntPtr

    End Function

    <DllImport(“user32.dll”, SetLastError:=True,

        CharSet:=CharSet.Auto)> _

    Public Shared Function MoveWindow(

        ByVal hWnd As IntPtr,

        ByVal x As Integer, ByVal y As Integer,

        ByVal nWidth As Integer,

        ByVal nHeight As Integer,

        ByVal bRepaint As Boolean) As Boolean

    End Function

    Private Sub Button1_Click(

            sender As Object, e As EventArgs) _

            Handles Button1.Click

        Me.IsMdiContainer = True

        Dim pi As New ProcessStartInfo(“NOTEPAD.EXE”)

        Dim p As Process = Process.Start(pi)

        Do While p.MainWindowHandle.Equals(IntPtr.Zero)

            Threading.Thread.Sleep(50)

            p.Refresh()

        Loop

        SetParent(p.MainWindowHandle, Me.Handle)

        MoveWindow(p.MainWindowHandle, 10, 5, 600, 500, True)

    End Sub

End Class

PROBLEMI CON ALCUNE APPLICAZIONI

La tecnica che vi ho mostrato funziona in molti casi, ma non in tutti: alcune applicazioni sono restìe ad accettare le nostre imposizioni e quindi si avviano nella solita modalità (per esempio a schermo intero).

Un esempio di questi è dato dalle applicazioni Office. Se provate questa tecnica con Word (eseguibile “WINWORD.EXE”) o con Excel (eseguibile “EXCEL.EXE”) vedrete che l’applicazione sarà, sì, avviata… ma sarà a tutto schermo. La nostra applicazione VB.NET, invece, segnalerà un’eccezione, perché a suo avviso l’applicazione è terminata prima di poter gestire la sua posizione.

In ogni caso, pur con le diverse peculiarità delle applicazioni, possiamo gestire molte applicazioni esterne dalla nostra applicazione .NET.

UN ULTIMO SPUNTO

Riflettendo sulla possibilità di avviare applicazioni esterne nelle nostre applicazioni, c’è un interessante risvolto: potremmo creare varie applicazioni (“moduli” di un applicativo complesso) da compilare singolarmente e poi richiamare come applicazioni esterne da un applicativo “principale”.

In questo modo potrebbe essere trovato il modo per gestire i vari moduli dell’applicativo e anche di creare dei moduli personalizzati da caricare in alternativa a quelli standard, in base al tipo di licenza dell’utente.

In questo ambito, comunque, entriamo in un’altra storia: quella della modularizzazione di un’applicazione.

Annunci

Pubblicato il 20 gennaio 2013 su Novità. Aggiungi ai preferiti il collegamento . 1 Commento.

  1. This is a great article, even through Google Translate!

    Mi piace

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...

%d blogger hanno fatto clic su Mi Piace per questo: