Migración de iPhone a Netbook: Más allá de los conceptos básicos

Más allá de los conceptos básicos

El desarrollo de iPhone ha ocurrido muy rápidamente debido a la existencia de la tienda de aplicaciones, pero también debido al gran número de funciones especializadas del dispositivo y las interfaces de alto nivel que Apple tiene disponibles. Ahora que los netbooks están teniendo más popularidad, gran parte del hardware que antes se relegaba a los smartphones (teléfonos inteligentes) se está usando en el mundo de los netbooks. De hecho, casi cada netbook vendido en la actualidad incluye una cámara, algo que ocurría muy raramente en un PC con Windows años atrás.

En los siguientes fragmentos hablaremos detalladamente de las diferencias del hardware entre el iPhone y los netbooks, además de incluir muestras de códigos para acceder a dichas funciones en cada plataforma (dónde y cuándo estén disponibles). Además de este artículo hay muy buenos recursos en la colección de artículos de ADP, que tratan técnicas de mayor envergadura utilizadas durante la migración. En esta entrevista en el artículo de ADP, titulada en inglés “From iPhone to AppUp: Porting of ‘Smiles’” (De iPhone a AppUp: Migración de sonrisas), Mike Kasprzack, Director Ejecutivo de Syhkronics, presenta muchas de las diferencias de hardware al explicar cómo él cambió su juego de iPhone a AppUp.

La biblioteca de referencia Apple iPhone OS (Apple iPhone OS Reference Library) y la biblioteca MSDN de Microsoft (MSDN Library) tienen abundantes recursos, tales como material de referencia, enseñanzas interactivas y temas tratados por la comunidad sobre cada plataforma.



Rotación y acelerómetro

Guía de consulta rápida

API de iPhone

API de Windows

Rotación de pantalla

didRotateFromInterfaceOrientation

DisplaySettingsChanged

Acelerómetro

UIAccelerometer

API de Windows Sensor

iPhone utiliza el acelerómetro integrado para detectar los cambios de orientación. iPhone API ofrece dos métodos para permitir y activar los cambios de orientación. shouldAutorotateToInterfaceOrientation indica al marco si debería girar automáticamente la interfaz del usuario cuando detecta la rotación del dispositivo. Si usted permite la rotación, el iPhone SDK activa didRotateFromInterfaceOrientation para permitir que la aplicación se ajuste al cambio de orientación.

Objective C de iPhone

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return YES;
}


- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    NSMutableString *newOrientation = [[[NSMutableString alloc] init] autorelease];
    switch ([[UIDevice currentDevice] orientation]) {
        case UIDeviceOrientationLandscapeLeft:
            [newOrientation setString:@"LandscapeLeft"];
            break;
        case UIDeviceOrientationLandscapeRight:
            [newOrientation setString:@"LandscapeRight"];
            break;
        case UIDeviceOrientationPortrait:
            [newOrientation setString:@"Portrait"];
            break;
        default:
            [newOrientation setString:@"(unknown)"];
            break;
    }

    [self setDiagInfo:@"Orientation" value:newOrientation];
}

Como afirma Kasprzack, “El bloqueo del juego en una sola orientación en PC es la solución normal.” Algunos notebooks (por lo general las tablets) apoyan la rotación de pantalla e indicarán al sistema cuando se gira la pantalla. 

Se puede detectar la rotación de pantalla al asignar un controlador de evento en C# (u otro código administrado) al evento de sistema DisplaySettingsChanged como muestra de ejemplo la siguiente aplicación Windows Presentation Foundation (WPF).

Windows XAML

<Window x:Class="Rotation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Rotation " Height="246" Width="431" Loaded="Window_Loaded" Unloaded="Window_Unloaded">
    <Grid>
        <Label Name="label1" Content="Orientation:" Height="28"  Width="131" 
               HorizontalAlignment="Left" HorizontalContentAlignment="Right" VerticalAlignment="Top" 
               Margin="12,44,0,0" FontSize="14" FontWeight="Bold" />
        <Label Name="labelOrientation" Content="(starting)" Height="28"  Width="230"
               HorizontalAlignment="Right" HorizontalContentAlignment="Left" VerticalAlignment="Top"
               Margin="0,44,30,0" FontSize="14" FontWeight="Bold" />
    </Grid>
</Window>

Windows C#

using System;
using System.Windows;
using Microsoft.Win32;

namespace Rotation
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void UpdateOrientation(object sender, EventArgs e)
        {
            double width = System.Windows.SystemParameters.PrimaryScreenWidth;
            double height = System.Windows.SystemParameters.PrimaryScreenHeight;

            string orientation;
            if (height > width)
                orientation = "Portrait";
            else
                orientation = "Landscape";

            string description = orientation + " (" + width.ToString() + "x" + height.ToString() + ")";
            labelOrientation.Content = description;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            SystemEvents.DisplaySettingsChanged += new EventHandler(UpdateOrientation);
            UpdateOrientation(sender, e);
        }

        private void Window_Unloaded(object sender, RoutedEventArgs e)
        {
            SystemEvents.DisplaySettingsChanged -= UpdateOrientation;
        }
    }

}

En C++ para aplicaciones nativas se escucha y responde al mensaje WM_DISPLAYCHANGE.

Windows C++

#include <windows.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM 
  lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;
    switch (message) 
    {
//. . .
//. . .
    case WM_DISPLAYCHANGE:
        {
        //Separate the screen width and height and then
        //check to see if screen width is greater than screen height
        if ((LOWORD(lParam) > HIWORD(lParam)) 
            //Run the application in landscape, for example:
            MessageBox(NULL,"Run in landscape.","Landscape",MB_OK);
        else
            //Run the application in portrait, as in:
            MessageBox(NULL,"Run in portrait.","Portrait",MB_OK);
        }
        break;

Los detalles completos, incluido esta muestra de código, se pueden encontrar en el artículo MSDN, “Detecting Screen Orientation and Screen Rotation in Tablet PC Applications” (Detección de orientación y rotación de pantalla en Tablet PC).

Si la aplicación depende del acelerómetro como entrada, en un juego por ejemplo, es posible que no sea adecuada para migración a un netbook debido a que la mayoría de los netbooks no incluyen un acelerómetro. Sin embargo, debido a que más tablets entraron en el mercado de mediados a fines del año 2010, espero que los acelerómetros se vuelvan más comunes. 

En el iPhone se puede obtener eventos de acelerómetro al registrarse con la clase UIAccelerometer como un delegado y consumidor de eventos.

Objective C de iPhone

-(void)viewDidLoad
{
    [super viewDidLoad];

    [[UIAccelerometer sharedAccelerometer] setUpdateInterval:1.0 / 60.0];
    [[UIAccelerometer sharedAccelerometer] setDelegate:self];
}

// UIAccelerometerDelegate method, called when the device accelerates.
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration

{
    // Do something with the data
    // acceleration.x, acceleration.y, acceleration.z
}

Windows 7 es compatible con los acelerómetros como un tipo de “sensor” mediante Sensor API, una interfaz COM accesible del código nativo C++. Para acceder a los sensores mediante el código administrado debe obtener Windows API Code Pack de MSDN. Contrario al iPhone, Windows no proporciona un API de alto nivel que extraiga datos de acelerómetro de dispositivos definidos. En la actualidad, los acelerómetros por lo general se encuentran en los dispositivos de bolsillo y Windows Mobile admite dos dispositivos de HTC y Samsung, cada uno mediante interfaces directas de hardware1

Para compatibilidad de los acelerómetros en las tablets o netbooks, deberá investigar el SDK o las interfaces publicadas por cada fabricante.

[1] Para obtener más detalles y el código fuente, consulte la versión no oficial sobre Windows Mobile Unified Sensor API, publicada por Koushik Dutta.

De la función táctil al ratón

A menos que esté usando gestos, los ratones responden casi igual que la función al tacto, excepto que las acciones del ratón ofrecen un evento de desplazamiento que la función al tacto no puede ofrecer. De modo que el paso de la interacción táctil al ratón debería ser simple. En base a la expectativa de los usuarios de Windows, es posible que le convenga agregar los efectos de desplazamiento si tiene componentes UI personalizados (como a menudo ocurre con los juegos). Al igual que los hipervínculos en los exploradores de web, la mayoría de los controles de Windows actualmente proporcionan datos visuales sobre el desplazamiento para indicar al usuario si el elemento responderá a un evento de clic. 

Si la aplicación admite gestos o depende de los gestos específicos al iPhone quizás deba reconsiderar cómo hacer que esto funcione en la aplicación de Windows. 

Windows Touch

Las tablets y los netbooks con pantalla táctil a menudo admiten gestos multitáctiles y Windows 7 Touch tiene compatibilidad integrada para arrastrar, desplazar, acercar/alejar, usar gestos, girar y muchos otros. Hay un buen artículo de MSDN al respecto, “Getting Started with Windows Touch Gestures" (Pasos iniciales con gestos de Windows Touch). Para dispositivos no táctiles, lo que sigue son maneras de administrar estos eventos.

Desplazar

Guía de consulta rápida

API de iPhone

API de Windows

Desplazamiento

UIScrollView

CWnd::OnMouseWheel

Control.OnMouseWheel

El gesto de desplazamiento en el iPhone funciona de forma similar a la rueda del ratón compatible con las aplicaciones de Windows, los controles de la barra de desplazamiento y todos los controles con el contenido de desplazamiento. En una situación personalizada de la interfaz del usuario, quizás deba capturar el evento scrollwheel (rueda de desplazamiento) para replicar esto. Por ejemplo, Google Maps en un explorador de web responde a la rueda de desplazamiento para acercar o alejar el mapa. En una aplicación de iPhone usted probablemente no tendrá que pensar sobre el desplazamiento porque es controlado por los componentes UI o implementado por una clase UIScrollView.

Observe que no hay equivalente de la acción ‘usar gesto’ en los controles estándares de Windows, pero lo más probable es que piense en esto como una invocación de avance o retroceso de página en un evento de barra de desplazamiento o podía inferirlo de la aceleración del evento de la rueda de desplazamiento. El evento se puede capturar al reemplazar el método CWnd::OnMouseWheel en C++/MFC o el método Control.OnMouseWheel en .NET

Windows C++

BOOL CWndEx::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)    
{      
    if( zDelta < 0 )   
    {   
        if( zDelta < -PAGE_DELTA ) {
            // Do action to "scroll" down one "line"
        } else {
            // Do action to "scroll" down one "page"
        }
    }   
    else   
    {   
        if( zDelta < PAGE_DELTA ) {
            // Do action to "scroll" up one "line"
        } else {
            // Do action to "scroll" up one "page"
        }
    }     
   
    return CWnd::OnMouseWheel(nFlags, zDelta, pt);   
}

Windows C#

protected override void OnMouseWheel(MouseWheelEventArgs e)
{ 
    if( e.Delta < 0 ) {
        if( e.Delta < -PAGE_DELTA ) {
            // Do action to "scroll" down one "line"
        } else {
            // Do action to "scroll" down one "page"
        }
    } else {
        if( e.Delta < PAGE_DELTA ) {
            // Do action to "scroll" up one "line"
        } else {
            // Do action to "scroll" up one "page"
        }
    }

}

Zoom (Acercar/Alejar)

Debido a que no hay compatibilidad nativa para una acción de alejar o de acercar en los dispositivos no táctiles usted necesitará decidir si la aplicación requiere esta acción. Podría haber otras maneras de proporcionar una capacidad de acercar/alejar similar quizás al agregar controles a la interfaz de usuario. Muchas aplicaciones de Windows incluyen un menú “Ver” con las opciones de menú “Acercar”, “Alejar” y “Tamaño normal”. Por lo general éstas se asignan a las pulsaciones de tecla Ctrl +, Ctrl - y Ctrl 0, respectivamente, y Windows 7 tiene las pulsaciones de tecla globales Windows + y Windows - para alejar o acercar toda la pantalla. 

Girar

El gesto de rotación de dos dedos que el iPhone reconoce tampoco es compatible nativamente en los dispositivos no táctiles para Windows. Vuelvo a repetir, por lo general las aplicaciones de Windows que permiten la rotación incluirán un menú "Ver" con los elementos “Rotate Clockwise” (Girar a la derecha) y “Rotate Counterclockwise” (Girar a la izquierda).

Arrastrar

Guía de consulta rápida

API de iPhone

API de Windows

Arrastrar y soltar

touchesMoved

DragDrop

Mientras que la función arrastrar y soltar no es nativamente compatible en iPhone (se tiene que implementar mediante el método touchesMoved), los eventos de arrastre son completamente compatibles con los API de Windows y están bien documentados. Existe un vídeo de 30 minutos en MSDN que describe cómo implementar la función de arrastrar y soltar en la aplicación MFC. Para .NET, consulte el artículo de MSDN, “Performing Drag-and-Drop Operations on Windows” ("Cómo realizar operaciones de arrastrar y soltar en Windows"), que proporciona el siguiente código de ejemplo:

Windows C#

private void button1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
   button1.DoDragDrop(button1.Text, DragDropEffects.Copy | 
      DragDropEffects.Move);
}

private void textBox1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
{
   if (e.Data.GetDataPresent(DataFormats.Text)) 
      e.Effect = DragDropEffects.Copy;
   else
      e.Effect = DragDropEffects.None;
}

private void textBox1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
{
   textBox1.Text = e.Data.GetData(DataFormats.Text).ToString();
}

Escalado de gráficos

El tamaño de la pantalla en un netbook es más grande que la pantalla de un iPhone. Si bien es cierto que los tamaños de pantalla de los netbooks varían, la gran mayoría de ellos son de 1024x600 píxeles. Para acomodar esta resolución de pantalla más grande y para acomodar distintas resoluciones de pantalla, le recomendamos ajustar el tamaño y la ubicación de objetos en base al tamaño de ventana o pantalla. Si está usando WPF para la aplicación de Windows, puede autoubicar objetos relacionados entre sí y escalarlos para que encajen en la ventana. Para aplicaciones con muchos gráficos, como los juegos, Kasprzack, en su entrevista, sugirió que se puede escalar gráficos comenzando con una resolución de referencia mayor que cualquiera de las resoluciones de destino. Al desarrollar las imágenes en este tamaño se pueden minimizar los gráficos adecuadamente a cada plataforma. Además, cualquier colocación o movimiento de objetos (tal como en los juegos), se puede escalar a cada resolución de dispositivo desde la resolución de referencia.

Reconocimiento de ubicación/GPS

Guía de consulta rápida

API de iPhone

API de Windows 7

Servicios de ubicación

Core Location Framework (Marco de ubicación central)

API de Ubicación

En las aplicaciones de iPhone, Apple incluyó el Core Location Framework que permite al desarrollador utilizar el hardware disponible para ubicar al usuario y rastrear los cambios a la ubicación del usuario, sin importar el hardware subyacente. La aplicación de muestra LocateMe en la biblioteca de referencia del iPhone ofrece un ejemplo de recopilación de la ubicación actual y notificaciones de los cambios a la ubicación. 

Para obtener una sola ubicación en iPhone, debe crear un controlador o delegado que implemente CLLocationManagerDelegate

Objective C de iPhone

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>

@interface LocationViewController : UIViewController <CLLocationManagerDelegate> {
    UILabel *labelLatitude;
    UILabel *labelLongitude;
    UILabel *labelStatus;
    CLLocationManager *locationManager;
}

@property(nonatomic,retain) IBOutlet UILabel *labelLatitude;
@property(nonatomic,retain) IBOutlet UILabel *labelLongitude;
@property(nonatomic,retain) IBOutlet UILabel *labelStatus;
@property(nonatomic,retain) CLLocationManager *locationManager;

- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation;

- (void)locationManager:(CLLocationManager *)manager
       didFailWithError:(NSError *)error;

@end

Entonces, se definen métodos para iniciar el servicio de localización, obtener el resultado y administrar cualquier error.

@implementation LocationViewController
@synthesize labelLatitude,labelLongitude,labelStatus,locationManager;

- (void)viewDidLoad {
    [super viewDidLoad];
    [labelStatus setText:@"Started"];

    locationManager = [[[CLLocationManager alloc] init] autorelease];
    locationManager.delegate = self;

    [locationManager startUpdatingLocation];
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation {
    [labelStatus setText:@"Got location"];
    [labelLatitude setText:[NSString stringWithFormat:@"%.6f",newLocation.coordinate.latitude]];
    [labelLongitude setText:[NSString stringWithFormat:@"%6f",newLocation.coordinate.longitude]];
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    [labelStatus setText:[error description]];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

- (void)viewDidUnload {
}

- (void)dealloc {
    [super dealloc];
}

@end

Con Windows 7 Location Platform, esto es casi tan fácil de realizar en los netbooks habilitados por ubicación. Observe que un gran número de netbooks actualmente no tienen la habilidad de encontrar la ubicación porque no tienen un sistema inalámbrico basado en celular o GPS. Hay un componente a aparecer próximamente en el catálogo de componentes (Component Catalog), en el sitio de ADP que encontrará la ubicación de un netbook en base al GPS integrado (u otro servicio de localización) o irá al sitio web y tratará de determinar la ubicación en base a la dirección IP del dispositivo. También podrá examinar los productos con licencia de Skyhook Wireless, quien proporciona un servicio de ubicación basado en Wifi, utilizado en la función táctil del iPod.

Si desea agregar el soporte de ubicación a las aplicaciones por su cuenta, puede usar API de ubicación de Windows para detectar los dispositivos de ubicación-identificación y obtener los datos de ellos. Aquí hay un snippet de código del mencionado componente para que comience a probarlo.

Windows C++

#include <LocationApi.h>

GeoLocation::GeoLocation() : Component(id) 
{    
    //Setup COM Location interface 
    HRESULT hr; 
    GeoCOM = false; 
    LOCATION_REPORT_STATUS locationReportStatus = REPORT_NOT_SUPPORTED;
    IID REPORTS[] = { IID_ILatLongReport, IID_ICivicAddressReport };
    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);

    if(SUCCEEDED(hr))
    {
        hr = pLocation.CoCreateInstance(CLSID_Location);

        if (SUCCEEDED(hr))
        {
            hr = pLocation->RequestPermissions(NULL, REPORTS, ARRAYSIZE(REPORTS), TRUE);
            if (SUCCEEDED(hr)) 
            {
                hr = pLocation->GetReportStatus(IID_ILatLongReport, &locationReportStatus);

                if (SUCCEEDED(hr))
                {
                    switch (locationReportStatus) 
                    {
                        case REPORT_NOT_SUPPORTED: 
                        case REPORT_ERROR:
                        case REPORT_ACCESS_DENIED:
                            GeoCOM = false; 
                        default: 
                            GeoCOM = true; 
                    }
                }
            }
        }
    }


    http = new HTTPRequest(); 
    locationData = new Location(); 

}

/** GeoLocation destructor
    frees the current location data and HTTPRequest class.  
*/ 
GeoLocation::~GeoLocation()
{

    //tear down com interface 
    CoUninitialize();

    if(locationData)
        delete locationData; 
    locationData = NULL; 
}

DWORD GeoLocation::GetCOMLocation()
{
    HRESULT hr = ERROR_SUCCESS; 

    CComPtr<ILocationReport> pLocationReport; 
    CComPtr<ILatLongReport> pLatLongReport; 
    DOUBLE dValue; 

    hr = pLocation->GetReport(IID_ILatLongReport, &pLocationReport);

    if(SUCCEEDED(hr))
    {

        if(pLocationReport)
            hr = pLocationReport->QueryInterface(&pLatLongReport);

        if(SUCCEEDED(hr))
        {
        
            hr = pLatLongReport->GetLongitude(&dValue); 
            if(SUCCEEDED(hr))
                locationData->dLongitude = dValue; 

            hr = pLatLongReport->GetLatitude(&dValue); 
            if(SUCCEEDED(hr))
                locationData->dLatitude = dValue; 

            hr = pLatLongReport->GetAltitude(&dValue); 
            if(SUCCEEDED(hr))
                locationData->dAltitude = dValue; 

            hr = pLatLongReport->GetAltitudeError(&dValue); 
            if(SUCCEEDED(hr))
                locationData->dAltitudeError = dValue; 

            hr = pLatLongReport->GetErrorRadius(&dValue); 
            if(SUCCEEDED(hr))
                locationData->dErrorRadius = dValue; 
        }
    }

    return hr; 
}

También se puede acceder al API de ubicación desde .NET y es mucho más simple. Aquí hay una aplicación completa para Windows con .NET y WPF que utiliza la clase GeoCoordinateWatcher para actualizar la pantalla cuando cambia la ubicación.

Windows XAML

<Window x:Class="Location.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Location" Height="238" Width="400">
    <Grid Height="177" Width="360">
        <Label Name="label1" Content="Latitude:"
               FontSize="18" FontWeight="Bold"
               Margin="12,23,0,0"
               HorizontalAlignment="Left" HorizontalContentAlignment="Right" VerticalAlignment="Top" Width="124" />

        <Label Name="label2" Content="Longitude:"
               FontSize="18" FontWeight="Bold"
               Margin="12,63,0,0"
               HorizontalAlignment="Left" HorizontalContentAlignment="Right" VerticalAlignment="Top" Width="124" />

        <Label Name="label3" Content="Status:"
               FontSize="18" FontWeight="Bold"
               Margin="12,103,0,0"
               HorizontalAlignment="Left" HorizontalContentAlignment="Right" VerticalAlignment="Top" Width="124" />

        <Label Name="labelLatitude" Content=""
               FontSize="18" FontWeight="Bold"
               Margin="142,23,0,0"
               HorizontalAlignment="Left" VerticalAlignment="Top" Width="206" />

        <Label Name="labelLongitude" Content=""
               FontSize="18" FontWeight="Bold"
               Margin="142,63,0,0"
               HorizontalAlignment="Left" VerticalAlignment="Top" Width="206" />

        <Label Name="labelStatus" Content="(starting)"
               FontSize="18" FontWeight="Bold"
               Margin="142,103,12,0" VerticalAlignment="Top" />


    </Grid>
</Window>

Windows C#

using System;
using System.Windows;
using System.Device.Location;

namespace Location
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            GeoCoordinateWatcher watcher = new GeoCoordinateWatcher();

            watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(LocationUpdate);
            if (watcher.TryStart(false, TimeSpan.FromMilliseconds(1000)))
                labelStatus.Content = "Started";
            else
                labelStatus.Content = "Couldn't start location";
        }

        public void LocationUpdate(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
        {
            labelStatus.Content = "Got location";
            labelLatitude.Content = e.Position.Location.Latitude.ToString();
            labelLongitude.Content = e.Position.Location.Longitude.ToString();
        }
    }
}

Almacenamiento de datos

Casi todas las aplicaciones deben retener el estado entre las sesiones. Algo de eso viene en forma de configuraciones de aplicación (por ej., encendido/apagado de sonido), mientras que otro almacenamiento de datos puede ser más grande (por ej., archivos multimedia, estado de reproducción de juego, datos recolectados). 

Datos pequeños

Guía de consulta rápida

API de iPhone

API de Windows

Configuración

Preferencias

Configuración de la aplicación

El iPhone tiene Preferencias para almacenar los datos de configuración más pequeños, que incluyen un API de alto nivel, lo cual facilita el almacenamiento de un valor designado simple para el usuario actual en el host actual.

Objective C de iPhone

@implementation SmallDataViewController
@synthesize binarySetting,stringSetting;

// Load settings from NSUserDefaults and update the display
- (void)viewDidLoad {
    NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
    [binarySetting setOn:[settings boolForKey:@"binarySetting"]];
    [stringSetting setText:[settings stringForKey:@"stringSetting"]];
    [super viewDidLoad];
}

// When the text box value is changed, update the settings and synchronize
- (IBAction) updateText:(id) sender {
    NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
    [settings setObject:stringSetting.text forKey:@"stringSetting"];
    [settings synchronize];
    [sender resignFirstResponder];
}

// When the switch is changed, update the settings and synchronize
- (IBAction) switchValueChanged:(id) sender {
    NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
    [settings setBool:binarySetting.on forKey:@"binarySetting"];
    [settings synchronize];
}

@end

Windows ofrece una gestión de la configuración de alto nivel en aplicaciones .NET a través de Application Settings (configuración de la aplicación). Esto puede hacerse directamente en Visual Studio mediante el diseñador u optar por escribir y las configuraciones de código por su parte, como en el siguiente ejemplo.

Windows XAML

<Window x:Class="SmallData.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SmallData" Height="182" Width="398" 
    >
    <Grid>
        <Label Name="label1" Content="Binary setting:" 
               HorizontalAlignment="Left"  VerticalAlignment="Top" Margin="25,23,0,0" 
               FontSize="16" FontWeight="Bold" />
        <Label Name="label2" Content="String setting:" 
               HorizontalAlignment="Left" VerticalAlignment="Top" Margin="27,78,0,0"
               FontSize="16" FontWeight="Bold" />
        <CheckBox Name="CBBinarySetting" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="164,32,0,0" />
        <TextBox Name="TBStringSetting" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="164,84,0,0" Width="175" />
    </Grid>
</Window>

Windows C#

using System;
using System.Configuration;

namespace SmallData
{
    class MyUserSettings : ApplicationSettingsBase
    {
        [UserScopedSetting()]
        [DefaultSettingValue("False")]
        public bool binarySetting
        {
            get { return ((bool)this["binarySetting"]); }

            set { this["binarySetting"] = (bool)value; }
        }

        [UserScopedSetting()]
        [DefaultSettingValue("")]
        public string stringSetting
        {
            get { return ((string)this["stringSetting"]); }

            set { this["stringSetting"] = (string)value; }
        }
    }
}

Windows C#

    
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace SmallData
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            MyUserSettings settings = new MyUserSettings();

            TBStringSetting.Text = settings.stringSetting;
            CBBinarySetting.IsChecked = settings.binarySetting;

            CBBinarySetting.Checked += CBBinarySetting_Changed;
            CBBinarySetting.Unchecked += CBBinarySetting_Changed;

            TBStringSetting.TextChanged += TBStringSetting_TextChanged;
        }

        private void CBBinarySetting_Changed(object sender, RoutedEventArgs e)
        {
            MyUserSettings settings = new MyUserSettings();
            settings.binarySetting = (bool)CBBinarySetting.IsChecked;
            settings.Save();
        }

        private void TBStringSetting_TextChanged(object sender, TextChangedEventArgs e)
        {
            MyUserSettings settings = new MyUserSettings();
            settings.stringSetting = TBStringSetting.Text;
            settings.Save();
        }
    }
}

Datos más grandes

Para datos de mayor tamaño como las bases de datos o los archivos, puede guardarlos localmente en el iPhone, en el almacenamiento dedicado, reservado para una aplicación de iPhone. La función NSSearchPathForDirectoriesInDomains se utiliza para ubicar el lugar de almacenamiento y luego se usan invocaciones estándares de IO para leer y escribir. Si necesita acceso de solo lectura a un archivo o base de datos, puede agruparlo con la aplicación como un Recurso en códigoX (Xcode).

Windows tiene menos limitación en cuanto a dónde se pueden almacenar los archivos (ciertas partes del sistema tienen acceso limitado en Windows 7). Sin embargo, estos días se considera que es buena práctica guardar archivos de solo lectura en la carpeta de archivos de programas de la aplicación, instalada con el paquete de instalación. Para archivos con capacidad de escritura y datos generados o recolectados por el usuario, la práctica común es almacenarlos en la carpeta AppData para la aplicación. Hay un número de API de alto nivel para ayudarlo a administrar diferentes casos de almacenamiento de datos. 

Afortunadamente, si ha incluido cualquier archivo de datos en la aplicación de iPhone, lo más común es que esté analizándolos o accediendo a ellos de manera similar en Windows. Por ejemplo, una base de datos SQLite podría leerse usando las interfaces C nativas en iPhone que se pueden copiar directamente en la aplicación de Windows y consumirse como una biblioteca C nativa. Si está pasando a .NET, puede usar la biblioteca System.Data.SQLite de otras compañías para asignar sus llamadas a .NET. En cuanto a la versión 2.6.3, el motor SQLite puede leer los archivos de su base de datos aun cuando ellos se hubieran creado en un sistema con un endian diferente.

Consumo de servicios web

Guía de consulta rápida

API de iPhone

API de Windows

HTTP Services

NSURLConnection

WinHTTP

HttpWebRequest

Un número considerable de aplicaciones de iPhone se conectan a los servicios web remotos y consumen datos o publican actualizaciones a través de ellos. Es posible que estos 'servicios' sean Servicios Web basados en SOAP, pero lo más probable es que sean interfaces RESTful o hasta simplemente conexiones HTTP que regresan y aceptan datos JSON, XML, HTML o estructurados.

El siguiente snippet utiliza la clase NSURLConnection para leer un URL y luego utiliza json-framework para Objective C de otras compañías para analizar la respuesta.

Objective C de iPhone

- (void)viewWillAppear:(BOOL)animated {
    NSURL *url = [NSURL URLWithString:urlString];
    NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60];
    [NSURLConnection connectionWithRequest:request delegate:self]
}

...

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [_receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    
    NSString* out = [[NSString alloc] initWithData:_receivedData encoding:NSUTF8StringEncoding];
    
    if ( out ) {
        SBJSON* jsonObject = [[SBJSON alloc] init];
        NSError* error;
        NSDictionary* outputDictionary = [jsonObject objectWithString:out error:&error];
        
        // Do something with the JSON response data
    } else {
        // Give an error or warning message
    }

    
    [_receivedData release];
    _receivedData = nil;
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog([NSString stringWithFormat:@"Request failed. Reason: %@", error.localizedDescription]);

    // Give an error or warning message
    
    [_receivedData release];
    _receivedData = nil;
}

Los servicios web basados en SOAP, aunque no sean compatibles de forma nativa in el SDK de iPhone, pueden ser usados en una aplicación de iPhone mediante una herramienta de terceros como wsdl2objc que genera clases de Objective C para acceder a servicios web basados en SOAP desde una definición de servicio WSDL.

Bajo Windows nativo, la biblioteca WinHTTP permite el acceso a un cliente para conectarse a un servidor de web. Debido a que las interacciones de red tienden a presentar errores, por lo general deseará incluir un buen número de capturas de error, poniendo a prueba todos los valores de retorno y actuando de manera adecuada para que la experiencia del usuario no se dificulte excesivamente en caso de que fallara cualquier parte de la solicitud HTTP. Dicho esto, aquí está el pseudocódigo para que pueda comenzar, que debería encapsularse en la administración de errores.

Windows C++

void HTTPRequest::Request(const std::wstring wsUrl, 
                const std::wstring wsHeaders, 
                const std::wstring wsContent,
                std::string &sOutBuffer)
{ 
    WinHttpCrackUrl( wsUrl.c_str(), wsUrl.length(), 0, &urlComponents );
    Url.assign(urlComponents.lpszHostName, urlComponents.dwHostNameLength);

    hConnect = WinHttpConnect( hSession, Url.c_str(), urlComponents.nPort, 0 );

    hRequest = WinHttpOpenRequest(
        hConnect, 
        "GET", 
        urlComponents.lpszUrlPath, 
        HTTP_VERSION.c_str(),
        WINHTTP_NO_REFERER, 
        WINHTTP_DEFAULT_ACCEPT_TYPES,
        dwOpenFlags);

    WinHttpSendRequest( hRequest, 
        wsHeaders.c_str(), 
        wsHeaders.length(),    
        WINHTTP_NO_REQUEST_DATA, 
        0, 
        0,
        0 ); 

    WinHttpReceiveResponse( hRequest, NULL );

    GetResponseData( hRequest, sOutBuffer); 
         
    if(hRequest)
        WinHttpCloseHandle(hRequest); 
    hRequest = NULL; 
    if(hConnect)
        WinHttpCloseHandle(hConnect); 
    hConnect = NULL; 
}

DWORD HTTPRequest::GetResponseData( HINTERNET hRequest, string &data) 
{
    DWORD dwSize; 
    DWORD dwStatus = ERROR_SUCCESS; 
    DWORD dwDownloaded;
    LPSTR pszOutBuffer;
        
    dwSize = 0;
    do {
        WinHttpQueryDataAvailable( hRequest, &dwSize); 
        pszOutBuffer = new char[dwSize+1];
        ZeroMemory(pszOutBuffer, dwSize + 1);
        WinHttpReadData( hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded);

        if(pszOutBuffer)
        {
            data.append(pszOutBuffer);
            delete [] pszOutBuffer;
            pszOutBuffer = NULL;
        }
    } while (dwSize > 0);

    
    if(pszOutBuffer)
        delete [] pszOutBuffer; 
    pszOutBuffer = NULL; 

    return dwStatus; 
}

Bajo .NET, esto es mucho más simple que con la clase HttpWebRequest.

Windows C#

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlString);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Encoding encoding = Encoding.GetEncoding(1252);  // Windows default Code Page
StreamReader reader = new StreamReader(response.GetResponseStream(),encoding);

string data = reader.ReadToEnd();

response.Close();
reader.Close();

Observe que en el caso nativo de Windows C++, el mecanismo HTTP es síncrono. La invocación del método bloquea la continuación del código hasta que se recibe la respuesta o vence el período de inactividad. Mientras que el ejemplo de .NET arriba también utiliza un enfoque síncrono, la clase incluye la solicitud/respuesta asíncrona. Bajo iPhone, la llamada es asíncrona de modo que no tiene que preocuparse del subproceso. Para el desempeño y respuesta siempre debería usar las llamadas asíncronas o colocar solicitudes de red en un subproceso distinto (para que no bloqueen el subproceso de la interfaz del usuario). Esto se cubre en la siguiente sección.

Subproceso y concurrencia

Guía de consulta rápida

API de iPhone

API de Windows

Subprocesos

Subprocesos tradicionales

Concurrencia mejorada

AfxBeginThread

ThreadStart

Muchas de las tareas a largo plazo que la biblioteca del iPhone presenta son asíncronas. Usted declara un delegado, inicia un evento y espera por una respuesta. Esto deja que el API se encargue del subproceso o la concurrencia de estos asuntos, lo cual significa una programación más veloz y un código más estable. Por lo tanto, la mayoría de los desarrolladores de iPhone no tienen que pensar en el subproceso, aunque Apple admite las opciones de subproceso tradicional y mejorado (y recomendado), tal como los objetos de operación y el Grand Central Dispatch (GCD, por sus siglas en inglés) en el iPhone.

Al mover las aplicaciones a Windows es posible que tenga que implementar sus propios subprocesos para administrar algunos eventos de manera asíncrona. Por ejemplo, una solicitud HTTP debería administrarse bajo un subproceso separado para que la interfaz del usuario todavía responda. Podrá lanzarla desde una aplicación MFC con la función AfxBeginThread.

Windows C++

typedef struct THREADINFOSTRUCT {
    HWND hWnd;
    bool bPush;
    std::wstring strUrl;
    int nApi;
} THREADINFOSTRUCT;

UINT __cdecl FnThreadedHttpRequest(LPVOID lParam) {
    THREADINFOSTRUCT* tis = (THREADINFOSTRUCT*)lParam;
    // Make the HTTP request and handle response
}

...

void App::CallApi(url) {
    THREADINFOSTRUCT* tis = new THREADINFOSTRUCT;
    tis->hWnd = hWnd;
    tis->bPush = false;
    tis->strUrl = url;
    tis->nApi = API_LOGIN;

    m_pHttpThread = AfxBeginThread(FnThreadedHttpRequest, tis, THREAD_PRIORITY_NORMAL, 0, 0);
}

Si está desarrollando una aplicación .NET, esta también se administra simplemente mediante el delegado ThreadStart entre los distintos enfoques.

Windows C#

static void CallApi(string url) 
{
    ThreadedHttpRequest worker = new ThreadedHttpRequest();
    worker.Push = false;
    worker.Url = url;
    worker.Api = API_LOGIN;

    ThreadStart threadDelegate = new ThreadStart(worker.DoRequest());    
    Thread thread = new Thread(threadDelegate);
    thread.Start();
}

class ThreadedHttpRequest
{
    public boolean Push; 
    public string Url;
    public int Api;

    public void DoRequest() {
        // Make Http request and handle response
    }
}

Recuerde que la programación de subprocesos múltiples es complicada. Cuando múltiples subprocesos acceden a las mismas partes del código o hacen cambios a los datos compartidos, usted puede encontrarse con condiciones de carrera y problemas de concurrencia. El subproceso es una herramienta poderosa para mejorar la respuesta y eficacia de su aplicación, siempre y cuando usted sincronice cuidadosamente su código. Donde se proporcionan interfaces asíncronas, le sugerimos usarlas para mejorar la seguridad del subproceso.

Lista de contactos

Guía de consulta rápida

API de iPhone

API de Windows

Libreta de direcciones

ABPeoplePickerNavigationController

API de Windows Live Contacts

Sus clientes de iPhone tienen una libreta de direcciones con todos sus contactos guardados en su teléfono. Si la aplicación de iPhone incluye capacidades de redes sociales, usted probablemente usó esto. Con la libreta de direcciones y los servicios relacionados usted puede utilizar la información de contacto para enviar correos, sms o compartir otra información.  

Por ejemplo, para obtener el nombre de un contacto seleccionado, usted usaría la clase ABPeoplePickerNavigationController e implementaría el protocolo ABPeoplePickerNavigationControllerDelegate en su clase.

Objective C de iPhone

@interface AddressBookQuickStartViewController : UIViewController <ABPeoplePickerNavigationControllerDelegate> {
    IBOutlet UILabel *firstName;
    IBOutlet UILabel *lastName;
}

@property (nonatomic, retain) UILabel *firstName;
@property (nonatomic, retain) UILabel *lastName;


- (IBAction) showPicker:(id) sender;

@end

@implementation AddressBookQuickStartViewController

@synthesize firstName;
@synthesize lastName;

-(IBAction) showPicker: (id) sender {
    ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
    picker.peoplePickerDelegate = self;
    
    [self presentModalViewController:picker animated:YES];
    [picker    release];
}


 
- (BOOL)peoplePickerNavigationController:
            (ABPeoplePickerNavigationController *)peoplePicker
      shouldContinueAfterSelectingPerson:(ABRecordRef)person {
 
    NSString* name = (NSString *)ABRecordCopyValue(person,
                                             kABPersonFirstNameProperty);
    self.firstName.text = name;
    [name release];
 
    name = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
    self.lastName.text = name;
    [name release];
 
    [self dismissModalViewControllerAnimated:YES];
 
    return NO;
}

Consulte Address Book UI Framework para obtener más información. Para acceso directo a la lista de contactos, puede utilizar Address Book Framework (Marco de la libreta de direcciones).

Para las aplicaciones de Windows podría usar el API de Windows Contacts, pero esta aplicación se ha reprobada en Windows 7 y lo más probable es que se reemplace con el API de Windows Live Contacts. Necesitará una conexión de Internet activa para poder acceder a la libreta de direcciones de Live Contacts. El API utiliza una interfaz REST al intercambiar archivos XML. Hay una aplicación de muestra que detalla esto en MSDN.

Cámara

Guía de consulta rápida

API de iPhone

API de Windows

Cámara

UIImagePickerController

DirectShow

La mayoría de los netbooks se envían con una cámara integrada. Esta es una buena noticia si ha desarrollado una aplicación de iPhone que utiliza la cámara y está decidiendo cómo migrarla a Windows. Sin embargo, la cámara en netbooks enfrenta al usuario (a menudo se encuentra en las especificaciones de producto como una webcam). En comparación con la cámara en el lado posterior de los iPhones (hasta la fecha, ese iPhone, iPhone 3G y iPhone 3GS), lo que significa que necesitará pensar cuidadosamente sobre la experiencia del usuario con su aplicación en la nueva plataforma. Por ejemplo, puede usar la cámara mirando hacia atrás en un iPhone para escanear un código de barra al sostener el teléfono por encima del código de barra. Con un netbook usted necesitará llevar el código de barras a la cámara. Sin embargo, la cámara en un netbook se puede usar para videoconferencias o cualquier otra aplicación donde el usuario esté tomando una foto de él mismo. Esto no es posible con la generación 3G de iPhones.

El iPhone le permite capturar fotos de la cámara y capturar video (en la versión 3.0 y posterior) con la clase UIImagePickerController e implementar un protocolo UIImagePickerControllerDelegate en su clase.

Objective C de iPhone

-(IBAction) recordVideo:(id) sender {
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
        UIImagePickerController * picker = [[UIImagePickerController alloc] init];
        picker.delegate = self;
        picker.mediaTypes = [NSArray arrayWithObject:(NSString *)kUTTypeMovie];
        picker.sourceType = UIImagePickerControllerSourceTypeCamera;
        [self presentModalViewController:picker animated:YES]
    }
}

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    [picker dismissModalViewControllerAnimated:YES];
    movieFileUrl = [info objectForKey:@"UIImagePickerControllerMediaURL"];
}

En Windows puede usarse DirectShow library para capturar video de la cámara y manipularlo durante el proceso. Primero deberá crear un gráfico de captura con el que configurar la transmisión en secuencia del archivo e iniciar el proceso de representación de video.

Windows C++

IBaseFilter *pMux;
hr = pBuild->SetOutputFileName(&MEDIASUBTYPE_Avi, L"C:\\Example.avi", &pMux, NULL);

hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pCap, NULL,  pMux);

pMux->Release();

Para tratar el tema más detalladamente, consulte la sección de Captura de video en la documentación de DirectShow, en MSDN.

Reproducción multimedia

El contenido multimedia como el video o el audio es ahora común y accesible desde los dispositivos de bolsillo a través de las redes 3G. El iPhone ofrece reproducción de video y audio, además de soporte para streaming, con interfaces de alto nivel para habilitar todas estas opciones. Al trasladar la aplicación a Windows tiene un número de opciones entre las que elegir para la reproducción multimedia, desde un reproductor integrado de alto nivel a acceso de bajo nivel a las transmisiones, permitiéndole hacer cambios a la experiencia. Dependiendo de sus necesidades, quizás desee considerar su opción de tecnología si la reproducción o el streaming de video o audio son críticas. Al utilizar WPF tendrá acceso inmediato Windows Media Player en su aplicación. Bajo C++, Windows Media Player podrá quedar integrado con un poco de scaffolding, pero usted tendrá un control más particular.

Reproducción de un video

Guía de consulta rápida

API de iPhone

API de Windows

Reproducción de video

UIImagePickerController

MediaElement

Una de las ventajas del contenido multimedia de iPhone es que es muy simple iniciar y reproducir un video.

Objective C de iPhone

MPMoviePlayerController *mp = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
if (mp)
{
    // save the movie player object
    self.moviePlayer = mp;
    [mp release];
        
    // Apply the user specified settings to the movie player object
    [self setMoviePlayerUserSettings];
        
    // Play the movie!
    [self.moviePlayer play];
}

Si puede crear su aplicación en C# usando WPF, entonces puede añadir fácilmente un MediaElement a su diseño y establecer el URL como fuente multimedia en XAML o en el código subyacente (como se muestra aquí)

Windows XAML

1
2
<MediaElement Name="myMediaElement" Width="450" Height="250" LoadedBehavior="Manual" UnloadedBehavior="Stop" Stretch="Fill"
MediaOpened="Element_MediaOpened" MediaEnded="Element_MediaEnded"/>

Windows C#

private void PlayContentUrl( Uri movieURL )
{
    myMediaElement.Source = movieURL;
    myMediaElement.Play();
}

La experiencia con Windows es un poco distinta y debería considerarla en su diseño. Para el iPhone, en la versión OS 3.1 y anteriores, el controlador MPMoviePlayerController se inicia de manera predeterminada, en modo de pantalla completa. Con MediaElement en WPF, el reproductor se integra en la aplicación, rodeado por los controles.

Si la aplicación de Windows está en C++, puede integrarse un control de Windows Media Player ActiveX en la aplicación y control, pero hay varios pasos que son necesarios. Usted puede leer un resumen de cada uno de los pasos en el artículo de MSDN, “Hosting the Windows Media Player Control in a Windows Application”.

Reproducción de audio

Guía de consulta rápida

API de iPhone

API de Windows

Reproducción de audio

AV Foundation framework

SoundPlayer

PlaySound

Si la aplicación de iPhone incorpora sonido, entonces probablemente esté familiarizado con el gran número de herramientas disponibles en iPhone para trabajar con el contenido de audio. Mientras que Media Player Framework le permite reproducir contenido desde la biblioteca del usuario de iPod, agregar sonidos a la aplicación puede ser tan simple como el AV Foundation Framework.

Objective C de iPhone

self._player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
if (self._player)
{
    [self._player play]
}

Si desea reproducir archivos de audio o reproducir audio vía transferencia en su aplicación Windows WPF, puede integrar MediaElement, como se indica en la discusión del video anterior; esto mostrará un reproductor multimedia. Si desea reproducir sonidos directamente en la aplicación .NET, sin elementos visuales, puede usar la clase SoundPlayer si los archivos de sonido están en formato .wav.

Windows C#

private void playSound() {
    SoundPlayer player = new SoundPlayer();
    player.SoundsLocation = pathToSoundFile;
    player.Play();
}

Para otro formato de archivo en WPF deberá integrar Windows Media Player, lo que puede hacer con la clase MediaPlayer. Esto es distinto a MediaElement porque no tiene una representación visual. Sin embargo, el API es similar.

Si está escribiendo su aplicación de Windows en C++, podría integrar Windows Media Player siguiendo la explicación anterior sobre el video que ofrece un elemento visual para reproducción. Si desea el sonido de fondo, puede usar la función PlaySound.

Windows C++

#include <msystem.h>

void MyClass::playSound() {
    PlaySound(fileUrl, NULL, SND_FILENAME | SND_ASYNC);
}

Observe que esto requiere un sonido que se pueda reproducir mediante el dispositivo de forma de onda en el netbook, de modo que reproducir archivos .wav probablemente sea lo más seguro.

0