Compartir a través de


Implementación de un proveedor de fuentes en una aplicación Win32 (C++/WinRT)

Nota:

Parte de la información hace referencia al producto de versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

Este artículo le guía a través de la creación de un proveedor de fuentes simple que registra un URI de contenido de fuente e implementa la interfaz IFeedProvider. El Panel de widgets invoca los métodos de esta interfaz para solicitar parámetros de cadena de consulta personalizados, normalmente para admitir escenarios de autenticación. Los proveedores de fuentes pueden admitir una sola fuente o varias fuentes.

Para implementar un proveedor de fuentes con C++/WinRT, consulte Implementación de un proveedor de fuentes en una aplicación Windows C# (C++/WinRT).

Requisitos previos

  • El dispositivo debe tener habilitado el modo de desarrollador. Para obtener más información, vea Habilitar el dispositivo para el desarrollo.
  • Visual Studio 2022 o posterior con la carga de trabajo Desarrollo de la Plataforma universal de Windows. Asegúrese de agregar el componente para C++ (v143) desde la lista desplegable opcional.

Creación de una aplicación de consola win32 de C++/WinRT

En Visual Studio, cree un nuevo proyecto. En el cuadro de diálogo Crear un nuevo proyecto, establezca el filtro de lenguaje en "C++" y el filtro de plataforma en Windows y, a continuación, seleccione la plantilla de proyecto Aplicación de consola de Windows (C++/WinRT). Asigne al nuevo proyecto el nombre "ExampleFeedProvider". Para este tutorial, asegúrese de que la opción Colocar la solución y el proyecto en el mismo directorio esté desactivada. Cuando se le solicite, establezca la versión de Windows de destino para la aplicación en 10.022631.2787 o posterior.

Adición de referencias a los paquetes NuGet Biblioteca de implementación de Windows y SDK de Aplicaciones para Windows

En este ejemplo se usa el paquete NuGet SDK de Aplicaciones para Windows estable más reciente. En Explorador de soluciones, haga clic con el botón derecho en Referencias y seleccione Administrar paquetes NuGet.... En el administrador de paquetes NuGet, seleccione la pestaña Examinar y busque "Microsoft.WindowsAppSDK". Seleccione la versión estable más reciente en la lista desplegable Versión y, a continuación, haga clic en Instalar.

En este ejemplo también se usa el paquete NuGet Biblioteca de implementación de Windows. En Explorador de soluciones, haga clic con el botón derecho en Referencias y seleccione Administrar paquetes NuGet.... En el administrador de paquetes NuGet, seleccione la pestaña Examinar y busque "Microsoft.Windows.ImplementationLibrary". Seleccione la versión más reciente en la lista desplegable Versión y, a continuación, haga clic en Instalar.

En el archivo de encabezado precompilado, pch.h, agregue las siguientes directivas de inclusión.

//pch.h 
#pragma once
#include <wil/cppwinrt.h>
#include <wil/resource.h>
...
#include <winrt/Microsoft.Windows.Widgets.Providers.h>

Nota

Debe incluir primero el encabezado wil/cppwinrt.h, antes de los encabezados de WinRT.

Para controlar el apagado correcto de la aplicación del proveedor de fuentes, necesitamos una implementación personalizada de winrt::get_module_lock. Declaramos previamente el método SignalLocalServerShutdown que se definirá en nuestro archivo main.cpp y establecerá un evento que indicará a la aplicación que debe cerrarse. Agregue el código siguiente al archivo pch.h, justo debajo de la directiva #pragma once, antes de los otros elementos de inclusión.

//pch.h
#include <stdint.h>
#include <combaseapi.h>

// In .exe local servers the class object must not contribute to the module ref count, and use
// winrt::no_module_lock, the other objects must and this is the hook into the C++ WinRT ref counting system
// that enables this.
void SignalLocalServerShutdown();

namespace winrt
{
    inline auto get_module_lock() noexcept
    {
        struct service_lock
        {
            uint32_t operator++() noexcept
            {
                return ::CoAddRefServerProcess();
            }

            uint32_t operator--() noexcept
            {
                const auto ref = ::CoReleaseServerProcess();

                if (ref == 0)
                {
                    SignalLocalServerShutdown();
                }
                return ref;
            }
        };

        return service_lock{};
    }
}


#define WINRT_CUSTOM_MODULE_LOCK

Adición de una clase FeedProvider para controlar las operaciones de fuente

En Visual Studio, haga clic con el botón derecho en el proyecto ExampleFeedProvider en Explorador de soluciones y seleccione Agregar->clase. En el cuadro de diálogo Agregar clase, asigne a la clase el nombre "FeedProvider" y, después, haga clic en Agregar.

Declaración de una clase que implementa la interfaz IFeedProvider

La interfaz IFeedProvider define los métodos que invocará el Panel de widgets para iniciar operaciones con el proveedor de fuentes. Reemplace la definición de clase vacía en el archivo FeedProvider.h por el código siguiente. Este código declara una estructura que implementa la interfaz IFeedProvider y declara prototipos para los métodos de interfaz.

// FeedProvider.h
#pragma once
struct FeedProvider : winrt::implements<FeedProvider, winrt::Microsoft::Windows::Widgets::Feeds::Providers::IFeedProvider>
{
    FeedProvider() {}

    /* IFeedrovider required functions that need to be implemented */
    void OnFeedProviderEnabled(winrt::Microsoft::Windows::Widgets::Feeds::Providers::FeedProviderEnabledArgs args);
    void OnFeedProviderDisabled(winrt::Microsoft::Windows::Widgets::Feeds::Providers::FeedProviderDisabledArgs args);
    void OnFeedEnabled(winrt::Microsoft::Windows::Widgets::Feeds::Providers::FeedEnabledArgs args);
    void OnFeedDisabled(winrt::Microsoft::Windows::Widgets::Feeds::Providers::FeedDisabledArgs args);
    void OnCustomQueryParametersRequested(winrt::Microsoft::Windows::Widgets::Feeds::Providers::CustomQueryParametersRequestedArgs args);
    /* IFeedProvider required functions that need to be implemented */

};

Implementación de los métodos IFeedProvider

En las secciones siguientes, implementaremos los métodos de la interfaz IFeedProvider. Antes de profundizar en los métodos de interfaz, agregue las siguientes líneas a FeedProvider.cpp, después de las directivas de inclusión, para extraer las API del proveedor de fuentes en el espacio de nombres winrt y permitir el acceso al mapa declarado en el paso anterior.

Nota:

Solo se garantiza que los objetos pasados a los métodos de devolución de llamada de la interfaz IFeedProvider sean válidos dentro de la devolución de llamada. No debe almacenar referencias a estos objetos porque su comportamiento fuera del contexto de la devolución de llamada no está definido.

// WidgetProvider.cpp
namespace winrt
{
    using namespace Microsoft::Windows::Widgets::Feeds::Providers;
}

OnFeedProviderEnabled

El método OnFeedProviderEnabled se invoca cuando el host del panel de widgets crea una fuente asociada al proveedor. En la implementación de este método, genere una cadena de consulta con los parámetros que se pasarán a la dirección URL que proporciona el contenido de la fuente, incluidos los tokens de autenticación necesarios. Cree una instancia de CustomQueryParametersUpdateOptions, pasando FeedProviderDefinitionId desde los argumentos del evento que identifica la fuente que se ha habilitado y la cadena de consulta. Obtenga el FeedManager predeterminado y llame a SetCustomQueryParameters para registrar los parámetros de cadena de consulta con el Panel de widgets.

// FeedProvider.cs
void FeedProvider::OnFeedProviderEnabled(winrt::Microsoft::Windows::Widgets::Feeds::Providers::FeedProviderEnabledArgs args)
{
    std::wstringstream wstringstream;
wstringstream << args.FeedProviderDefinitionId().c_str() << L" feed provider was enabled." << std::endl;
    _putws(wstringstream.str().c_str());

    auto updateOptions = winrt::CustomQueryParametersUpdateOptions(args.FeedProviderDefinitionId(), L"param1&param2");
    winrt::FeedManager::GetDefault().SetCustomQueryParameters(updateOptions);
}

OnFeedProviderDisabled

Se llama a OnFeedProviderDisabled cuando se ha deshabilitado el panel de widgets cuando se han deshabilitado todas las fuentes de este proveedor. No es necesario que los proveedores de fuentes realicen ninguna acción en respuesta a esta llamada de método. La invocación del método se puede usar con fines de telemetría o para actualizar los parámetros de cadena de consulta o revocar tokens de autenticación, si es necesario. Si la aplicación solo admite un único proveedor de fuentes o si todos los proveedores de fuentes admitidos por la aplicación se han deshabilitado, la aplicación puede salir en respuesta a esta devolución de llamada.

// FeedProvider.cs

void FeedProvider::OnFeedProviderDisabled(winrt::Microsoft::Windows::Widgets::Feeds::Providers::FeedProviderDisabledArgs args)
{
    std::wstringstream wstringstream;
    wstringstream << args.FeedProviderDefinitionId().c_str() << L" feed provider was disabled." << std::endl;
    _putws(wstringstream.str().c_str());
}

OnFeedEnabled, OnFeedDisabled

OnFeedEnabled y OnFeedDisabled se invocan mediante el Panel de widgets cuando una fuente está habilitada o deshabilitada. No es necesario que los proveedores de fuentes realicen ninguna acción en respuesta a estas llamadas de método. La invocación del método se puede usar con fines de telemetría o para actualizar los parámetros de cadena de consulta o revocar tokens de autenticación, si es necesario.

// FeedProvider.cs

void FeedProvider::OnFeedEnabled(winrt::Microsoft::Windows::Widgets::Feeds::Providers::FeedEnabledArgs args)
{
    std::wstringstream wstringstream;
    wstringstream << args.FeedDefinitionId().c_str() << L" feed was enabled." << std::endl;
    _putws(wstringstream.str().c_str());
}
// FeedProvider.cs

void FeedProvider::OnFeedDisabled(winrt::Microsoft::Windows::Widgets::Feeds::Providers::FeedDisabledArgs args)
{
    std::wstringstream wstringstream;
    wstringstream << args.FeedDefinitionId().c_str() << L" feed was disabled." << std::endl;
    _putws(wstringstream.str().c_str());
}

OnCustomQueryParametersRequested

OnCustomQueryParametersRequested se genera cuando el Panel de widgets determina que es necesario actualizar los parámetros de consulta personalizados asociados al proveedor de fuentes. Por ejemplo, este método se puede generar si se produce un error en la operación para capturar contenido de fuente del servicio web remoto. La propiedad FeedProviderDefinitionId de CustomQueryParametersRequestedArgs que se pasa a este método especifica la fuente para la que se solicitan parámetros de cadena de consulta. El proveedor debe volver a generar la cadena de consulta y volver a pasarla al Panel de widgets llamando a SetCustomQueryParameters.

// FeedProvider.cs

void FeedProvider::OnCustomQueryParametersRequested(winrt::Microsoft::Windows::Widgets::Feeds::Providers::CustomQueryParametersRequestedArgs args)
{
    std::wstringstream wstringstream;
    wstringstream << L"CustomQueryParameters were requested for " << args.FeedProviderDefinitionId().c_str() << std::endl;
    _putws(wstringstream.str().c_str());

    auto updateOptions = winrt::CustomQueryParametersUpdateOptions(args.FeedProviderDefinitionId(), L"param1&param2");
    winrt::FeedManager::GetDefault().SetCustomQueryParameters(updateOptions);
}

Registro de un generador de clases que creará una instancia de FeedProvider a petición

Agregue el encabezado que define la clase FeedProvider a los elementos que se incluyen en la parte superior del archivo main.cpp de la aplicación. También incluiremos exclusión mutua aquí.

// main.cpp
...
#include "FeedProvider.h"
#include <mutex>

Declare el evento que desencadenará la salida de la aplicación y la función SignalLocalServerShutdown que establecerá el evento. Pegue el código siguiente en main.cpp.

// main.cpp
wil::unique_event g_shudownEvent(wil::EventOptions::None);

void SignalLocalServerShutdown()
{
    g_shudownEvent.SetEvent();
}

A continuación, deberá crear un CLSID que se usará para identificar el proveedor de fuentes para la activación COM. Para generar un GUID en Visual Studio, vaya a Herramientas->Crear GUID. Seleccione la opción "static const GUID =" y haga clic en Copiar; a continuación, péguela en main.cpp. Actualice la definición de GUID con la siguiente sintaxis de C++/WinRT, estableciendo el nombre de la variable GUID feed_provider_clsid. Deje la versión comentada del GUID porque necesitará este formato más adelante, al empaquetar la aplicación.

// main.cpp
...
// {80F4CB41-5758-4493-9180-4FB8D480E3F5}
static constexpr GUID feed_provider_clsid
{
    0x80f4cb41, 0x5758, 0x4493, { 0x91, 0x80, 0x4f, 0xb8, 0xd4, 0x80, 0xe3, 0xf5 }
};

Añade la siguiente definición de fábrica de clases a main.cpp. Esto es principalmente código reutilizable que no es específico de las implementaciones del proveedor de fuentes. Tenga en cuenta que CoWaitForMultipleObjects espera a que se desencadene el evento de apagado antes de que se cierre la aplicación.

// main.cpp
template <typename T>
struct SingletonClassFactory : winrt::implements<SingletonClassFactory<T>, IClassFactory>
{
    STDMETHODIMP CreateInstance(
        ::IUnknown* outer,
        GUID const& iid,
        void** result) noexcept final
    {
        *result = nullptr;

        std::unique_lock lock(mutex);

        if (outer)
        {
            return CLASS_E_NOAGGREGATION;
        }

        if (!instance)
        {
            instance = winrt::make<FeedProvider>();
        }

        return instance.as(iid, result);
    }

    STDMETHODIMP LockServer(BOOL) noexcept final
    {
        return S_OK;
    }

private:
    T instance{ nullptr };
    std::mutex mutex;
};

int main()
{
    winrt::init_apartment();
    wil::unique_com_class_object_cookie feedProviderFactory;
    auto factory = winrt::make<SingletonClassFactory<winrt::Microsoft::Windows::Widgets::Feeds::Providers::IFeedProvider>>();

    winrt::check_hresult(CoRegisterClassObject(
        feed_provider_clsid,
        factory.get(),
        CLSCTX_LOCAL_SERVER,
        REGCLS_MULTIPLEUSE,
        feedProviderFactory.put()));

    DWORD index{};
    HANDLE events[] = { g_shudownEvent.get() };
    winrt::check_hresult(CoWaitForMultipleObjects(CWMO_DISPATCH_CALLS | CWMO_DISPATCH_WINDOW_MESSAGES,
        INFINITE,
        static_cast<ULONG>(std::size(events)), events, &index));

    return 0;
}

Empaquetar la aplicación del proveedor de fuentes

En la versión actual, solo las aplicaciones empaquetadas se pueden registrar como proveedores de fuentes. Los pasos siguientes le llevarán a través del proceso de empaquetado de la aplicación y la actualización del manifiesto de aplicación para registrar la aplicación con el sistema operativo como proveedor de fuentes.

Creación de un proyecto de empaquetado MSIX

En Explorador de soluciones, haga clic con el botón derecho en la solución y seleccione Agregar->Nuevo proyecto.... En el cuadro de diálogo Agregar un nuevo proyecto, seleccione la plantilla "Proyecto de paquete de aplicación de Windows" y haga clic en Siguiente. Establezca el nombre del proyecto en "ExampleFeedProviderPackage" y haga clic en Crear. Cuando se le solicite, establezca el destino en la versión 1809 o posterior y haga clic en Aceptar. A continuación, haga clic con el botón derecho en el proyecto ExampleFeedProviderPackage y seleccione Agregar->Referencia de proyecto. Seleccione el proyecto ExampleFeedProvider y haga clic en Aceptar.

Adición de la referencia del paquete SDK de Aplicaciones para Windows al proyecto de empaquetado

Debe agregar una referencia al paquete Nuget SDK de Aplicaciones para Windows al proyecto de empaquetado MSIX. En Explorador de soluciones, haga doble clic en el proyecto ExampleFeedProviderPackage para abrir el archivo ExampleFeedProviderPackage.wapproj. Agregue el siguiente XML dentro del elemento Proyecto.

<!--ExampleFeedProviderPackage.wapproj-->
<ItemGroup>
    <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.231116003-experimentalpr">
        <IncludeAssets>build</IncludeAssets>
    </PackageReference>  
</ItemGroup>

Nota

Asegúrese de que la versión especificada en el elemento PackageReference coincide con la versión estable más reciente a la que se hizo referencia en el paso anterior.

Si la versión correcta del SDK de Aplicaciones para Windows ya está instalada en el equipo y no desea agrupar el entorno de ejecución del SDK en el paquete, puede especificar la dependencia del paquete en el archivo Package.appxmanifest para el proyecto ExampleFeedProviderPackage.

<!--Package.appxmanifest-->
...
<Dependencies>
...
    <PackageDependency Name="Microsoft.WindowsAppRuntime.1.5.233430000-experimental1" MinVersion="2000.638.7.0" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
...
</Dependencies>
...

Actualización del manifiesto del paquete

En el Explorador de soluciones, haga clic con el botón derecho en el archivo Package.appxmanifest y seleccione Ver código para abrir el archivo xml del manifiesto. A continuación, debe agregar algunas declaraciones de espacio de nombres para las extensiones de paquete de la aplicación que usaremos. Agregue las siguientes definiciones de espacio de nombres al elemento de nivel superior Package.

<!-- Package.appmanifest -->
<Package
  ...
  xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
  xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"

Dentro del elemento Application, cree un nuevo elemento vacío denominado Extensions. Asegúrese de que aparece después de la etiqueta de cierre de uap:VisualElements.

<!-- Package.appxmanifest -->
<Application>
...
    <Extensions>

    </Extensions>
</Application>

La primera extensión que necesitamos agregar es ComServer. Registra el punto de entrada del ejecutable en el sistema operativo. Esta extensión es el equivalente en aplicaciones empaquetadas a registrar un servidor COM estableciendo una clave del Registro, y no es específica de los proveedores de fuentes. Agregue el siguiente elemento com:Extension como elemento secundario del elemento Extensions. Cambie el GUID en el atributo Id del elemento com:Class por el GUID que generó en un paso anterior.

<!-- Package.appxmanifest -->
<Extensions>
    <com:Extension Category="windows.comServer">
        <com:ComServer>
            <com:ExeServer Executable="ExampleFeedProvider\ExampleFeedProvider.exe" Arguments="-RegisterProcessAsComServer" DisplayName="C++ Feed Provider App">
                <com:Class Id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" DisplayName="FeedProvider" />
            </com:ExeServer>
        </com:ComServer>
    </com:Extension>
</Extensions>

A continuación, agregue la extensión que registra la aplicación como proveedor de fuentes. Pegue el elemento uap3:Extension en el siguiente fragmento de código, como elemento secundario del elemento Extensions. Asegúrese de reemplazar el atributo ClassId del elemento COM por el GUID que usó en los pasos anteriores.

<!-- Package.appxmanifest -->
<Extensions>
    ...
    <uap3:Extension Category="windows.appExtension">
        <uap3:AppExtension Name="com.microsoft.windows.widgets.feeds" DisplayName="ContosoFeed" Id="com.examplewidgets.examplefeed" PublicFolder="Public">
            <uap3:Properties>
                <FeedProvider Icon="ms-appx:Assets\StoreLogo.png" Description="FeedDescription">
                    <Activation>
                        <!-- Apps exports COM interface which implements IFeedProvider -->
                        <CreateInstance ClassId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
                    </Activation>
                    <Definitions>
                        <Definition Id="Contoso_Feed"
                            DisplayName="Contoso_Feed Feed"
                            Description="Feed representing Contoso"
                            ContentUri="https://www.contoso.com/"
                            Icon="ms-appx:Images\StoreLogo.png">
                        </Definition>
                        <Definition Id="Fabrikam_Feed"
                            DisplayName="Fabrikam Feed"
                            Description="Feed representing Example"
                            ContentUri="https://www.fabrikam.com/"
                            Icon="ms-appx:Images\StoreLogo.png">
                        </Definition>
                    </Definitions>
                </FeedProvider>
            </uap3:Properties>
        </uap3:AppExtension>
    </uap3:Extension>
</Extensions>

Para obtener descripciones detalladas e información de formato para todos estos elementos, vea Formato XML del manifiesto de paquete del proveedor de fuentes.

Adición de iconos al proyecto de empaquetado

En el Explorador de soluciones, haga clic con el botón derecho en ExampleFeedProviderPackage y seleccione Agregar->Nueva carpeta. Asigne a esta carpeta el nombre ProviderAssets, ya que es lo que se utilizó en Package.appxmanifest del paso anterior. Aquí es donde almacenaremos nuestro Icono para nuestras fuentes. Una vez que agregue los iconos deseados, asegúrese de que los nombres de imagen coinciden con lo que viene después de Path=ProviderAssets\ en Package.appxmanifest o las fuentes no se mostrarán en el Panel de widgets.

Probar un proveedor de fuentes

Asegúrese de que ha seleccionado la arquitectura que coincide con la máquina de desarrollo en la lista desplegable Plataformas de soluciones, por ejemplo, "x64". En el Explorador de soluciones, haga clic con el botón secundario en la solución y seleccione Generar solución. Una vez hecho esto, haga clic con el botón derecho en ExampleWidgetProviderPackage y seleccione Implementar. La aplicación de consola debe iniciarse en la implementación y verá que las fuentes se habilitan en la salida de la consola. Abra el Panel de widgets y debería ver las nuevas fuentes en las pestañas de la parte superior de la sección de fuentes.

Depurar un proveedor de fuentes

Después de anclar las fuentes, la plataforma de widgets iniciará la aplicación del proveedor de fuentes para recibir y enviar información pertinente sobre la fuente. Para depurar la fuente en ejecución, puede asociar un depurador a la aplicación del proveedor de fuentes en ejecución o configurar Visual Studio para que inicie automáticamente la depuración del proceso del proveedor de fuentes una vez iniciado.

Para asocia un elemento al proceso en ejecución:

  1. En Visual Studio, haga clic en Depurar->Asociar al proceso.
  2. Filtre los procesos y busque la aplicación del proveedor de fuentes deseada.
  3. Asocie el depurador.

Para asociar automáticamente el depurador al proceso cuando se inicia por primera vez:

  1. En Visual Studio, haga clic en Depurar -> Otros destinos de depuración -> Depurar paquete de aplicación instalado.
  2. Filtre los paquetes y busque el paquete de proveedor de fuentes deseado.
  3. Selecciónelo y active la casilla que indica No iniciar, pero depurar mi código cuando se inicie.
  4. Haga clic en Adjuntar.

Conversión de la aplicación de consola en una aplicación de Windows

Para convertir la aplicación de consola creada en este tutorial en una aplicación de Windows:

  1. Haga clic con el botón derecho en el proyecto ExampleWidgetProvider en el Explorador de soluciones y seleccione Propiedades. Vaya a Vinculador -> Sistema y cambie Subsistema de "Consola" a "Windows". Esto también se puede hacer agregando <SubSystem>Windows</SubSystem> a la sección <Link>..</Link> del .vcxproj.
  2. En main.cpp, cambie int main() a int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, _In_opt_ HINSTANCE /*hPrevInstance*/, _In_ PWSTR pCmdLine, _In_ int /*nCmdShow*/).

Captura de pantalla que muestra las propiedades del proyecto del proveedor de fuentes de C++ con el tipo de salida establecido en Aplicación de Windows

Publicación de la aplicación del proveedor de fuentes

Después de haber desarrollado y probado el proveedor de fuentes, puede publicar la aplicación en Microsoft Store para que los usuarios instalen las fuentes en sus dispositivos. Para obtener instrucciones paso a paso para publicar una aplicación, consulta Publicación de la aplicación en Microsoft Store.

La colección feeds Store

Una vez publicada la aplicación en Microsoft Store, puedes solicitar que la aplicación se incluya en la colección de la Tienda feeds que ayuda a los usuarios a detectar aplicaciones que cuentan con fuentes de Windows. Para enviar la solicitud, consulta Enviar tu feed/Board para agregar la colección de la Tienda.