Índice de la sección dedicada a .NET (en el Guille)   .NET Compact Framework

Ejecutar una aplicación
Normal y una creada con eMbedded Visual Basic
Y activar y cerrar una aplicación (con título)


Publicado el 08/Ene/2003
Revisado el 10/May/2003


En este artículo vamos a ver el código necesario para ejecutar una aplicación "normal" así como el que tendríamos que usar si esa aplicación es una creada con Visual Basic eMbedded, (eVB), ya que estos últimos "ejecutables" realmente no son ejecutables como los que estamos acostumbrados, además de que dichos "ejecutables" tienen la extensión .vb
También veremos, sino iba a ser un artículo demasiado corto, cómo activar una aplicación existente y cómo cerrarla, en estos dos últimos casos necesitaremos saber el título de la ventana a cerrar, mientras que en el primero, lo que necesitamos es saber la localización del ejecutable.

Para ejecutar o "lanzar" una aplicación, vamos a usar la función del API CreateProcess, la implementación que te voy a mostrar es la más sencilla y en la que no necesita información extra.
Para ver el detalle de esta función, puedes mostrar este artículo de la ayuda de Visual Studio .NET 2003:
ms-help://MS.VSCC...1033/wcekernl/htm/_wcesdk_Win32_CreateProcess.htm
(el texto en negrita tendrás que ajustarlo al formato de la ayuda instalada)

Nota:
Si te preguntas porqué usar un API cuando el .NET Framework tiene clases para trabajar con los procesos, te diré que el .NET Compact Framework (.NET CF), no tiene esa clase disponible.
Si has llegado aquí buscando un ejemplo para el .NET Framework normal, te recomiendo que veas este otro artículo de la sección Cómo en .NET: Mostrar los procesos activos, iniciarlos y detenerlos.

 

El código para Visual Basic .NET

 

Veamos la declaración de la función CreateProcess y cómo podemos usarla en nuestra aplicación para el Pocket PC:

<DllImport("CoreDll.DLL")> _
Private Function CreateProcess( _
    ByVal imageName As String, _
    ByVal cmdLine As String, _
    ByVal lpProcessAttributes As Integer, _
    ByVal lpThreadAttributes As Integer, _
    ByVal boolInheritHandles As Int32, _
    ByVal dwCreationFlags As Int32, _
    ByVal lpEnvironment As Integer, _
    ByVal lpszCurrentDir As Integer, _
    ByVal si As Integer, _
    ByVal pi As Integer) As Integer
End Function

De todos los parámetros que esta función implementa, sólo nos interesan los dos primeros: el nombre del ejecutable y los parámetros, el resto no nos es necesario, por tanto, lo mejor es que creemos nuestra propia versión de esta función, para que sea más cómodo usarla.
Aunque podríamos crear las nuevas versiones de la función usando el mismo nombre, ya que el .NET CF utilizaría la que se adecuara a los parámetros indicados, voy a crear dos funciones llamadas Execute, una de ellas recibirá dos parámetros: el nombre del ejecutable y los parámetros a usar con el ejecutable y otra en la que sólo se indicará el nombre del ejecutable a "arrancar".
Veamos el código:

Public Sub Execute(ByVal exeName As String, _
                   ByVal cmdLine As String)
    CreateProcess(exeName, cmdLine, 0, 0, 0, 0, 0, 0, 0, 0)
End Sub

Public Sub Execute(ByVal exeName As String)
    Execute(exeName, "")
End Sub

En estos dos procedimientos (sobrecargados) lo único que hacemos es llamar a la función del API, realmente sólo lo hacemos en uno de ellos, ya que el segundo llama a la implementación que recibe los dos parámetros, aunque también lo podríamos haber hecho de esta otra forma:

Public Sub Execute(ByVal exeName As String)
    CreateProcess(exeName, "", 0, 0, 0, 0, 0, 0, 0, 0)
End Sub

Elige la que más te apetezca, aunque yo particularmente, cuando utilizo métodos sobrecargados, prefiero llamar al procedimiento que más parámetros recibe desde los que reciben menos...


Ejecutar (o abrir) una aplicación normal:

La forma de usar estas funciones sería bastante simple, si por ejemplo queremos abrir el Internet Explorer, podemos hacer lo siguiente:
Execute("iexplore.exe")
o bien indicando el path completo, para los casos de que el ejecutable no esté en un directorio incluido en el path:
Execute("\Windows\iexplore.exe")


Ejecutar una aplicación de Visual Basic eMbedded:

Para poder ejecutar una aplicación de Visual Basic eMbedded, habría que indicar como programa a ejecutar el runtime del eVB y como parámetro el nombre de la aplicación creada con dicho entorno de desarrollo:
Execute("\Windows\pvbload.exe", "progEVB.vb")

 

Activar/cerrar una aplicación que se esté ejecutando

Para activar y/o cerrar una aplicación que esté en ejecución, ésta debe tener un título o un texto en la ventana, en estos dos casos, necesitaremos otras funciones del API:
FindWindow para saber el "handler" de la ventana,
SetForegroundWindow para activarla y
SendMessage para cerrarla.

Empecemos viendo las declaraciones de estas funciones del API y después veremos el código de unos procedimientos que se encargarán de activarla y cerrarla.

<System.Runtime.InteropServices.DllImport("coredll.dll", _
        EntryPoint:="FindWindow")> _
Public Function FindWindow( _
        ByVal lpClassName As String, _
        ByVal lpWindowName As String) As IntPtr
End Function
'
<System.Runtime.InteropServices.DllImport("coredll.dll", _
        EntryPoint:="SetForegroundWindow")> _
Public Function SetForegroundWindow( _
        ByVal hWnd As IntPtr) As Boolean
End Function
'
<System.Runtime.InteropServices.DllImport("coredll.dll", _
        EntryPoint:="SendMessage")> _
Public Function SendMessage( _
        ByVal hWnd As IntPtr, ByVal wMsg As Integer, _
        ByVal wParam As Integer, ByVal lParam As Integer) As Integer
End Function


El procedimiento de cerrar la aplicación será el siguiente:

Private Const WM_SYSCOMMAND As Integer = &H112
Private Const SC_CLOSE As Integer = &HF060
'
Public Sub CloseApp(ByVal title As String)
    ' Cerrar la ventana indicada, mediante el menú del sistema (o de windows)
    ' Esto funcionará si la aplicación tiene menú de sistema
    ' (aunque lo he probado con una utilidad sin controlBox y la cierra bien)
    '
    '
    Dim hWnd As IntPtr = FindWindow(Nothing, title)

    ' si no está en memoria, mostrar un error
    If hWnd.Equals(IntPtr.Zero) Then
        MessageBox.Show("No se ha encontrado la aplicación:" & vbCrLf & title)
    Else
        ' la activamos
        SetForegroundWindow(hWnd)
        ' la cerramos enviándole un mensaje de cerrar
        Call SendMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0)
    End If
End Sub

Lo que aquí hacemos es buscar la ventana con el título indicado, si no está funcionando se muestra un mensaje de error y en el caso de que esté funcionando, la traemos al frente (activamos) y después le enviamos un mensaje de cerrar, para ello se utiliza la función SendMessage.

En cuanto al código de activar la ventana es muy parecido al de cerrar, ya que se siguen los mismos pasos y lo único que no se hace es precisamente cerrarla, por tanto el código sería este:

Public Sub AppActivate(ByVal title As String)
    ' activamos la ventana con el título indicado

    Dim hWnd As IntPtr = FindWindow(Nothing, title)
    '
    ' si no está en memoria, mostrar un error
    If hWnd.Equals(IntPtr.Zero) Then
        MessageBox.Show("No se ha encontrado la aplicación:" & vbCrLf & title)
    Else
        ' la activamos
        SetForegroundWindow(hWnd)
    End If
End Sub


Y esto es todo.
Si quieres saber cómo aplicar estas últimas funciones a una aplicación para que sólo haya una copia funcionando en el sistema, es decir que se active la copia en funcionamiento si la ejecutamos nuevamente, puedes ver lo dicho en el artículo para simular PrevInstance.

 

Nos vemos.
Guillermo


El código para C#

Aquí sólo te muestro el código de C#, los comentarios los puedes ver en la parte del código de VB.

//
[DllImport("CoreDll.DLL")]
private static extern int CreateProcess(
    String imageName,
    String cmdLine,
    int lpProcessAttributes,
    int lpThreadAttributes,
    Int32 boolInheritHandles,
    Int32 dwCreationFlags,
    int lpEnvironment,
    int lpszCurrentDir,
    int si,
    int pi);
//
public static void Execute(String exeName, String cmdLine)
{
    CreateProcess(exeName, cmdLine, 0, 0, 0, 0, 0, 0, 0, 0);
}  
public static void Execute(String exeName)
{
    //CreateProcess(exeName, "", 0, 0, 0, 0, 0, 0, 0, 0);
    Execute(exeName, "");
}

 

//
[DllImport("coredll.dll", EntryPoint="SetForegroundWindow")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
//
[DllImport("coredll.dll", EntryPoint="FindWindow")]
public static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
//
[DllImport("coredll.dll", EntryPoint="SendMessage")]
public static extern int SendMessage(IntPtr hWnd, int wMsg, int wParam, int lParam);
//
const int WM_SYSCOMMAND  = 0x112;
const int SC_CLOSE  = 0xF060;
//
public static void CloseApp(String title)
{
    // Cerrar la ventana indicada, mediante el menú del sistema (o de windows)
    // Esto funcionará si la aplicación tiene menú de sistema
    // (aunque lo he probado con una utilidad sin controlBox y la cierra bien)
    //
    IntPtr hWnd = FindWindow(null, title);
    //
    // si no está en memoria, mostrar un error
    if( hWnd.Equals(IntPtr.Zero) )
    {
        MessageBox.Show("No se ha encontrado la aplicación:\n" + title);
    }
    else
    {
        // la activamos
        SetForegroundWindow(hWnd);
        // la cerramos enviándole un mensaje de cerrar
        SendMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0);
    }
}  
//
public static void AppActivate(String title)
{
    // activamos la ventana con el título indicado
    IntPtr hWnd = FindWindow(null, title);
    //
    // si no está en memoria, mostrar un error
    if( hWnd.Equals(IntPtr.Zero) )
    {
        MessageBox.Show("No se ha encontrado la aplicación:\n" + title);
    }
    else
    {
        // la activamos
        SetForegroundWindow(hWnd);
    }
}

 


la Luna del Guille o... el Guille que está en la Luna... tanto monta...