Creación y registro de una tarea en segundo plano com de Win32
Sugerencia
El método BackgroundTaskBuilder.SetTaskEntryPointClsid está disponible a partir de Windows 10, versión 2004.
Nota:
Este escenario solo es aplicable a las aplicaciones Win32 empaquetadas. Las aplicaciones para UWP encontrarán errores al intentar implementar este escenario.
API importantes
Cree una clase de tarea en segundo plano COM y regístrela para que se ejecute en la aplicación Win32 empaquetada de plena confianza en respuesta a los desencadenadores. Puedes usar tareas en segundo plano para proporcionar funcionalidad cuando la aplicación está suspendida o no en ejecución. En este tema se muestra cómo crear y registrar una tarea en segundo plano que se puede ejecutar en el proceso de aplicación en primer plano u otro proceso.
Crear la clase Tarea en segundo plano
Puede ejecutar código en segundo plano escribiendo clases que implementan la interfaz IBackgroundTask. Este código se ejecuta cuando se desencadena un evento específico mediante, por ejemplo, SystemTrigger o TimeTrigger.
Los pasos siguientes muestran cómo escribir una nueva clase que implemente la interfaz IBackgroundTask y agregarla al proceso principal.
- Consulte estas instrucciones para hacer referencia a las API de WinRT en la solución de aplicación Win32 empaquetada. Esto es necesario para usar IBackgroundTask y las API relacionadas.
- En esa nueva clase, implemente la interfaz IBackgroundTask. El método IBackgroundTask.Run es un punto de entrada necesario al que se llamará cuando se desencadene el evento especificado; este método es necesario en cada tarea en segundo plano.
Nota:
La propia clase de tarea en segundo plano ,y todas las demás clases del proyecto de tarea en segundo plano, deben ser públicas.
El código de ejemplo siguiente muestra una clase de tarea básica en segundo plano que cuenta los primos y lo escribe en un archivo hasta que se solicita que se cancele.
El ejemplo de C++/WinRT implementa la clase de tarea en segundo plano como una coclase COM.
Ejemplo de código de tarea en segundo plano
using System;
using System.IO; // Path
using System.Threading; // EventWaitHandle
using System.Collections.Generic; // Queue
using System.Runtime.InteropServices; // Guid, RegistrationServices
using Windows.ApplicationModel.Background; // IBackgroundTask
namespace PackagedWinMainBackgroundTaskSample
{
// {14C5882B-35D3-41BE-86B2-5106269B97E6} is GUID to register this task with BackgroundTaskBuilder. Generate a random GUID before implementing.
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("14C5882B-35D3-41BE-86B2-5106269B97E6")]
[ComSourceInterfaces(typeof(IBackgroundTask))]
public class SampleTask : IBackgroundTask
{
private volatile int cleanupTask; // flag used to indicate to Run method that it should exit
private Queue<int> numbersQueue; // the data structure holding the set of primes in memory
private const int maxPrimeNumber = 1000000000; // the number up to which task will attempt to calculate primes
private const int queueDepthToWrite = 10; // how frequently this task should flush its queue of primes
private const string numbersQueueFile = "numbersQueue.log"; // the file to write to relative to AppData
public SampleTask()
{
cleanupTask = 0;
numbersQueue = new Queue<int>(queueDepthToWrite);
}
/// <summary>
/// This method writes all the numbers in the current queue to the specified file.
/// </summary>
private void FlushNumbersToFile(Queue<int> queueToWrite)
{
string logPath = Path.Combine(ApplicationData.Current.LocalFolder.Path,
System.Diagnostics.Process.GetCurrentProcess().ProcessName);
if (!Directory.Exists(logPath))
{
Directory.CreateDirectory(logPath);
}
logPath = Path.Combine(logPath, numbersQueueFile);
const string delimiter = ", ";
UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
// convert the queue to a list of comma separated values.
string stringToWrite = String.Join(delimiter, queueToWrite);
// Add the comma at the end.
stringToWrite += delimiter;
File.AppendAllText(logPath, stringToWrite);
}
/// <summary>
/// This method determines if the specified number is a prime number.
/// </summary>
private bool IsPrimeNumber(int dividend)
{
bool isPrime = true;
for (int divisor = dividend - 1; divisor > 1; divisor -= 1)
{
if ((dividend % divisor) == 0)
{
isPrime = false;
break;
}
}
return isPrime;
}
/// <summary>
/// Given the current number, this method calculates the next prime number (excluding the specified number).
/// </summary>
private int GetNextPrime(int previousNumber)
{
int currentNumber = previousNumber + 1;
while (!IsPrimeNumber(currentNumber))
{
currentNumber += 1;
}
return currentNumber;
}
/// <summary>
/// This method is the main entry point for the background task. The system will believe this background task
/// is complete when this method returns.
/// </summary>
[MTAThread]
public void Run(IBackgroundTaskInstance taskInstance)
{
// Start with the first applicable number.
int currentNumber = 1;
taskDeferral = taskInstance.GetDeferral();
// Wire the cancellation handler.
taskInstance.Canceled += this.OnCanceled;
// Set the progress to indicate this task has started
taskInstance.Progress = 10;
// Calculate primes until a cancellation has been requested or until
// the maximum number is reached.
while ((cleanupTask == 0) && (currentNumber < maxPrimeNumber)) {
// Compute the next prime number and add it to our queue.
currentNumber = GetNextPrime(currentNumber);
numbersQueue.Enqueue(currentNumber);
// Once the queue is filled to its max size, flush the numbers to the file.
if (numbersQueue.Count >= queueDepthToWrite)
{
FlushNumbersToFile(numbersQueue);
numbersQueue.Clear();
}
}
// Flush any remaining numbers to the file as part of cleanup.
FlushNumbersToFile(numbersQueue);
if (taskDeferral != null)
{
taskDeferral.Complete();
}
}
/// <summary>
/// This method is signaled when the system requests the background task be canceled. This method will signal
/// to the Run method to clean up and return.
/// </summary>
[MTAThread]
public void OnCanceled(IBackgroundTaskInstance taskInstance, BackgroundTaskCancellationReason cancellationReason)
{
cleanupTask = 1;
}
}
}
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.ApplicationModel.Background.h>
using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::ApplicationModel::Background;
namespace PackagedWinMainBackgroundTaskSample {
// Note insert unique UUID.
struct __declspec(uuid("14C5882B-35D3-41BE-86B2-5106269B97E6"))
SampleTask : implements<SampleTask, IBackgroundTask>
{
const unsigned int MaximumPotentialPrime = 1000000000;
volatile bool isCanceled = false;
BackgroundTaskDeferral taskDeferral = nullptr;
void __stdcall Run (_In_ IBackgroundTaskInstance taskInstance)
{
taskInstance.Canceled({ this, &SampleTask::OnCanceled });
taskDeferral = taskInstance.GetDeferral();
unsigned int currentPrimeNumber = 1;
while (!isCanceled && (currentPrimeNumber < MaximumPotentialPrime))
{
currentPrimeNumber = GetNextPrime(currentPrimeNumber);
}
taskDeferral.Complete();
}
void __stdcall OnCanceled (_In_ IBackgroundTaskInstance, _In_ BackgroundTaskCancellationReason)
{
isCanceled = true;
}
};
struct TaskFactory : implements<TaskFactory, IClassFactory>
{
HRESULT __stdcall CreateInstance (_In_opt_ IUnknown* aggregateInterface, _In_ REFIID interfaceId, _Outptr_ VOID** object) noexcept final
{
if (aggregateInterface != NULL) {
return CLASS_E_NOAGGREGATION;
}
return make<SampleTask>().as(interfaceId, object);
}
HRESULT __stdcall LockServer (BOOL) noexcept final
{
return S_OK;
}
};
}
Adición del código de soporte técnico para crear una instancia de la clase COM
Para que la tarea en segundo plano se active en una aplicación Win32 de plena confianza, la clase de tarea en segundo plano debe tener código de soporte para que COM comprenda cómo iniciar el proceso de la aplicación si no se está ejecutando y, a continuación, comprender qué instancia del proceso es actualmente el servidor para controlar nuevas activaciones para esa tarea en segundo plano.
- COM debe comprender cómo iniciar el proceso de la aplicación si aún no se está ejecutando. El proceso de aplicación que hospeda el código de tarea en segundo plano debe declararse en el manifiesto del paquete. En el código de ejemplo siguiente se muestra cómo se hospeda SampleTask dentro de SampleBackgroundApp.exe. Cuando se inicia la tarea en segundo plano cuando no se ejecuta ningún proceso, SampleBackgroundApp.exe se iniciará con los argumentos de proceso "-StartSampleTaskServer".
<Extensions>
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:ExeServer Executable="SampleBackgroundApp\SampleBackgroundApp.exe" DisplayName="SampleBackgroundApp" Arguments="-StartSampleTaskServer">
<com:Class Id="14C5882B-35D3-41BE-86B2-5106269B97E6" DisplayName="Sample Task" />
</com:ExeServer>
</com:ComServer>
</com:Extension>
</Extensions>
- Una vez que el proceso se inicia con los argumentos correctos, debe indicar a COM que es el servidor COM actual para las nuevas instancias de SampleTask. En el código de ejemplo siguiente se muestra cómo el proceso de aplicación debe registrarse en COM. Tenga en cuenta que estos ejemplos indican cómo el proceso se declararía como el servidor COM para SampleTask para que al menos una instancia se complete antes de salir. Esto es opcional y controlar una tarea en segundo plano puede iniciar las funciones principales del proceso.
class SampleTaskServer
{
SampleTaskServer()
{
comRegistrationToken = 0;
waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
}
~SampleTaskServer()
{
Stop();
}
public void Start()
{
RegistrationServices registrationServices = new RegistrationServices();
comRegistrationToken = registrationServices.RegisterTypeForComClients(typeof(SampleTask), RegistrationClassContext.LocalServer, RegistrationConnectionType.MultipleUse);
// Either have the background task signal this handle when it completes, or never signal this handle to keep this
// process as the COM server until the process is closed.
waitHandle.WaitOne();
}
public void Stop()
{
if (comRegistrationToken != 0)
{
RegistrationServices registrationServices = new RegistrationServices();
registrationServices.UnregisterTypeForComClients(registrationCookie);
}
waitHandle.Set();
}
private int comRegistrationToken;
private EventWaitHandle waitHandle;
}
var sampleTaskServer = new SampleTaskServer();
sampleTaskServer.Start();
class SampleTaskServer
{
public:
SampleTaskServer()
{
waitHandle = EventWaitHandle(false, EventResetMode::AutoResetEvent);
comRegistrationToken = 0;
}
~SampleTaskServer()
{
Stop();
}
void Start()
{
try
{
com_ptr<IClassFactory> taskFactory = make<TaskFactory>();
winrt::check_hresult(CoRegisterClassObject(__uuidof(SampleTask),
taskFactory.get(),
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
&comRegistrationToken));
// Either have the background task signal this handle when it completes, or never signal this handle to
// keep this process as the COM server until the process is closed.
waitHandle.WaitOne();
}
catch (...)
{
// Indicate an error has been encountered.
}
}
void Stop()
{
if (comRegistrationToken != 0)
{
CoRevokeClassObject(comRegistrationToken);
}
waitHandle.Set();
}
private:
DWORD comRegistrationToken;
EventWaitHandle waitHandle;
};
SampleTaskServer sampleTaskServer;
sampleTaskServer.Start();
Registrar la tarea en segundo plano para ejecutar
- Averigüe si la tarea en segundo plano ya está registrada iterando a través de la propiedad BackgroundTaskRegistration.AllTasks. Este paso es importante; si la aplicación no comprueba los registros de tareas en segundo plano existentes, podría registrar fácilmente la tarea varias veces, lo que provoca problemas con el rendimiento y el máximo del tiempo de CPU disponible de la tarea antes de que se pueda completar el trabajo. Una aplicación es libre de usar el mismo punto de entrada para controlar todas las tareas en segundo plano y usar otras propiedades como el Nombre o TaskId asignado a un BackgroundTaskRegistration para decidir qué trabajo debe realizarse.
En el ejemplo siguiente se itera en la propiedad AllTasks y se establece una variable de marca en true si la tarea ya está registrada.
var taskRegistered = false;
var sampleTaskName = "SampleTask";
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == sampleTaskName)
{
taskRegistered = true;
break;
}
}
// The code in the next step goes here.
bool taskRegistered = false;
std::wstring sampleTaskName = L"SampleTask";
auto allTasks = BackgroundTaskRegistration::AllTasks();
for (auto const& task : allTasks)
{
if (task.Value().Name() == sampleTaskName)
{
taskRegistered = true;
break;
}
}
// The code in the next step goes here.
- Si la tarea en segundo plano aún no está registrada, use BackgroundTaskBuilder para crear una instancia de la tarea en segundo plano. El punto de entrada de la tarea debe ser el nombre de la clase de tarea en segundo plano prefijo por el espacio de nombres .
El desencadenador de tareas en segundo plano controla cuándo se ejecutará la tarea en segundo plano. Para obtener una lista de posibles desencadenadores, consulte el Namespace Windows.ApplicationModel.Background.
Nota:
Solo se admite un subconjunto de desencadenadores para tareas en segundo plano de Win32 empaquetadas.
Por ejemplo, este código crea una nueva tarea en segundo plano y la establece para ejecutarla en un TimeTrigger periódico de 15 minutos:
if (!taskRegistered)
{
var builder = new BackgroundTaskBuilder();
builder.Name = sampleTaskName;
builder.SetTaskEntryPointClsid(typeof(SampleTask).GUID);
builder.SetTrigger(new TimeTrigger(15, false));
}
// The code in the next step goes here.
if (!taskRegistered)
{
BackgroundTaskBuilder builder;
builder.Name(sampleTaskName);
builder.SetTaskEntryPointClsid(__uuidof(SampleTask));
builder.SetTrigger(TimeTrigger(15, false));
}
// The code in the next step goes here.
- Puede agregar una condición para controlar cuándo se ejecutará la tarea después de que se produzca el evento desencadenador (opcional). Por ejemplo, si no desea que la tarea se ejecute hasta que Internet esté disponible, use la condición InternetAvailable. Para obtener una lista de las posibles condiciones, consulte SystemConditionType.
El código de ejemplo siguiente asigna una condición que requiere que el usuario esté presente:
builder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));
// The code in the next step goes here.
builder.AddCondition(SystemCondition{ SystemConditionType::InternetAvailable });
// The code in the next step goes here.
- Registre la tarea en segundo plano llamando al método Register en el objeto BackgroundTaskBuilder. Almacene el resultado BackgroundTaskRegistration para que se pueda usar en el paso siguiente. Tenga en cuenta que la función register puede devolver errores en forma de excepciones. Asegúrese de llamar a Register en un try-catch.
El código siguiente registra la tarea en segundo plano y almacena el resultado:
try
{
var task = builder.Register();
}
catch (...)
{
// Indicate an error was encountered.
}
try
{
auto task = builder.Register();
}
catch (...)
{
// Indicate an error was encountered.
}
Reunirlo todo
Los ejemplos de código siguientes muestran el código completo necesario para ejecutar y registrar la tarea en segundo plano com Win32:
Completar el manifiesto del paquete de la aplicación Win32
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
IgnorableNamespaces="uap rescap com">
<Identity
Name="SamplePackagedWinMainBackgroundApp"
Publisher="CN=Contoso"
Version="1.0.0.0" />
<Properties>
<DisplayName>SamplePackagedWinMainBackgroundApp</DisplayName>
<PublisherDisplayName>Contoso</PublisherDisplayName>
<Logo>Images\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="SampleBackgroundApp\$targetnametoken$.exe"
EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="SampleBackgroundApp"
Description="SampleBackgroundApp"
BackgroundColor="transparent"
Square150x150Logo="Images\Square150x150Logo.png"
Square44x44Logo="Images\Square44x44Logo.png">
<uap:DefaultTile Wide310x150Logo="Images\Wide310x150Logo.png" />
<uap:SplashScreen Image="Images\SplashScreen.png" />
</uap:VisualElements>
<Extensions>
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:ExeServer Executable="SampleBackgroundApp\SampleBackgroundApp.exe" DisplayName="SampleBackgroundApp" Arguments="-StartSampleTaskServer">
<com:Class Id="14C5882B-35D3-41BE-86B2-5106269B97E6" DisplayName="Sample Task" />
</com:ExeServer>
</com:ComServer>
</com:Extension>
</Extensions>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>
Ejemplo de código de tarea en segundo plano completo
using System;
using System.IO; // Path
using System.Threading; // EventWaitHandle
using System.Collections.Generic; // Queue
using System.Runtime.InteropServices; // Guid, RegistrationServices
using Windows.ApplicationModel.Background; // IBackgroundTask
namespace PackagedWinMainBackgroundTaskSample
{
// Background task implementation.
// {14C5882B-35D3-41BE-86B2-5106269B97E6} is GUID to register this task with BackgroundTaskBuilder. Generate a random GUID before implementing.
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("14C5882B-35D3-41BE-86B2-5106269B97E6")]
[ComSourceInterfaces(typeof(IBackgroundTask))]
public class SampleTask : IBackgroundTask
{
private volatile int cleanupTask; // flag used to indicate to Run method that it should exit
private Queue<int> numbersQueue; // the data structure holding the set of primes in memory
private const int maxPrimeNumber = 1000000000; // the number up to which task will attempt to calculate primes
private const int queueDepthToWrite = 10; // how frequently this task should flush its queue of primes
private const string numbersQueueFile = "numbersQueue.log"; // the file to write to relative to AppData
public SampleTask()
{
cleanupTask = 0;
numbersQueue = new Queue<int>(queueDepthToWrite);
}
/// <summary>
/// This method writes all the numbers in the current queue to the specified file.
/// </summary>
private void FlushNumbersToFile(Queue<int> queueToWrite)
{
string logPath = Path.Combine(ApplicationData.Current.LocalFolder.Path,
System.Diagnostics.Process.GetCurrentProcess().ProcessName);
if (!Directory.Exists(logPath))
{
Directory.CreateDirectory(logPath);
}
logPath = Path.Combine(logPath, numbersQueueFile);
const string delimiter = ", ";
UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
// convert the queue to a list of comma separated values.
string stringToWrite = String.Join(delimiter, queueToWrite);
// Add the comma at the end.
stringToWrite += delimiter;
File.AppendAllText(logPath, stringToWrite);
}
/// <summary>
/// This method determines if the specified number is a prime number.
/// </summary>
private bool IsPrimeNumber(int dividend)
{
bool isPrime = true;
for (int divisor = dividend - 1; divisor > 1; divisor -= 1)
{
if ((dividend % divisor) == 0)
{
isPrime = false;
break;
}
}
return isPrime;
}
/// <summary>
/// Given the current number, this method calculates the next prime number (excluding the specified number).
/// </summary>
private int GetNextPrime(int previousNumber)
{
int currentNumber = previousNumber + 1;
while (!IsPrimeNumber(currentNumber))
{
currentNumber += 1;
}
return currentNumber;
}
/// <summary>
/// This method is the main entry point for the background task. The system will believe this background task
/// is complete when this method returns.
/// </summary>
[MTAThread]
public void Run(IBackgroundTaskInstance taskInstance)
{
// Start with the first applicable number.
int currentNumber = 1;
taskDeferral = taskInstance.GetDeferral();
// Wire the cancellation handler.
taskInstance.Canceled += this.OnCanceled;
// Set the progress to indicate this task has started
taskInstance.Progress = 10;
// Calculate primes until a cancellation has been requested or until
// the maximum number is reached.
while ((cleanupTask == 0) && (currentNumber < maxPrimeNumber)) {
// Compute the next prime number and add it to our queue.
currentNumber = GetNextPrime(currentNumber);
numbersQueue.Enqueue(currentNumber);
// Once the queue is filled to its max size, flush the numbers to the file.
if (numbersQueue.Count >= queueDepthToWrite)
{
FlushNumbersToFile(numbersQueue);
numbersQueue.Clear();
}
}
// Flush any remaining numbers to the file as part of cleanup.
FlushNumbersToFile(numbersQueue);
if (taskDeferral != null)
{
taskDeferral.Complete();
}
}
/// <summary>
/// This method is signaled when the system requests the background task be canceled. This method will signal
/// to the Run method to clean up and return.
/// </summary>
[MTAThread]
public void OnCanceled(IBackgroundTaskInstance taskInstance, BackgroundTaskCancellationReason cancellationReason)
{
cleanupTask = 1;
}
}
// COM server startup code.
class SampleTaskServer
{
SampleTaskServer()
{
comRegistrationToken = 0;
waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
}
~SampleTaskServer()
{
Stop();
}
public void Start()
{
RegistrationServices registrationServices = new RegistrationServices();
comRegistrationToken = registrationServices.RegisterTypeForComClients(typeof(SampleTask), RegistrationClassContext.LocalServer, RegistrationConnectionType.MultipleUse);
// Either have the background task signal this handle when it completes, or never signal this handle to keep this
// process as the COM server until the process is closed.
waitHandle.WaitOne();
}
public void Stop()
{
if (comRegistrationToken != 0)
{
RegistrationServices registrationServices = new RegistrationServices();
registrationServices.UnregisterTypeForComClients(registrationCookie);
}
waitHandle.Set();
}
private int comRegistrationToken;
private EventWaitHandle waitHandle;
}
// Background task registration code.
class SampleTaskRegistrar
{
public static void Register()
{
var taskRegistered = false;
var sampleTaskName = "SampleTask";
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == sampleTaskName)
{
taskRegistered = true;
break;
}
}
if (!taskRegistered)
{
var builder = new BackgroundTaskBuilder();
builder.Name = sampleTaskName;
builder.SetTaskEntryPointClsid(typeof(SampleTask).GUID);
builder.SetTrigger(new TimeTrigger(15, false));
}
try
{
var task = builder.Register();
}
catch (...)
{
// Indicate an error was encountered.
}
}
}
// Application entry point.
static class Program
{
[MTAThread]
static void Main()
{
string[] commandLineArgs = Environment.GetCommandLineArgs();
if (commandLineArgs.Length < 2)
{
// Open the WPF UI when no arguments are specified.
}
else
{
if (commandLineArgs.Contains("-RegisterSampleTask", StringComparer.InvariantCultureIgnoreCase))
{
SampleTaskRegistrar.Register();
}
if (commandLineArgs.Contains("-StartSampleTaskServer", StringComparer.InvariantCultureIgnoreCase))
{
var sampleTaskServer = new SampleTaskServer();
sampleTaskServer.Start();
}
}
return;
}
}
}
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.ApplicationModel.Background.h>
using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::ApplicationModel::Background;
namespace PackagedWinMainBackgroundTaskSample
{
// Background task implementation.
// {14C5882B-35D3-41BE-86B2-5106269B97E6} is GUID to register this task with BackgroundTaskBuilder. Generate a random GUID before implementing.
struct __declspec(uuid("14C5882B-35D3-41BE-86B2-5106269B97E6"))
SampleTask : implements<SampleTask, IBackgroundTask>
{
const unsigned int maxPrimeNumber = 1000000000;
volatile bool isCanceled = false;
BackgroundTaskDeferral taskDeferral = nullptr;
void __stdcall Run (_In_ IBackgroundTaskInstance taskInstance)
{
taskInstance.Canceled({ this, &SampleTask::OnCanceled });
taskDeferral = taskInstance.GetDeferral();
unsigned int currentPrimeNumber = 1;
while (!isCanceled && (currentPrimeNumber < maxPrimeNumber))
{
currentPrimeNumber = GetNextPrime(currentPrimeNumber);
}
taskDeferral.Complete();
}
void __stdcall OnCanceled (_In_ IBackgroundTaskInstance, _In_ BackgroundTaskCancellationReason)
{
isCanceled = true;
}
};
struct TaskFactory : implements<TaskFactory, IClassFactory>
{
HRESULT __stdcall CreateInstance (_In_opt_ IUnknown* aggregateInterface, _In_ REFIID interfaceId, _Outptr_ VOID** object) noexcept final
{
if (aggregateInterface != nullptr) {
return CLASS_E_NOAGGREGATION;
}
return make<SampleTask>().as(interfaceId, object);
}
HRESULT __stdcall LockServer (BOOL) noexcept final
{
return S_OK;
}
};
// COM server startup code.
class SampleTaskServer
{
public:
SampleTaskServer()
{
waitHandle = EventWaitHandle(false, EventResetMode::AutoResetEvent);
comRegistrationToken = 0;
}
~SampleTaskServer()
{
Stop();
}
void Start()
{
try
{
com_ptr<IClassFactory> taskFactory = make<TaskFactory>();
winrt::check_hresult(CoRegisterClassObject(__uuidof(SampleTask),
taskFactory.get(),
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
&comRegistrationToken));
// Either have the background task signal this handle when it completes, or never signal this handle to
// keep this process as the COM server until the process is closed.
waitHandle.WaitOne();
}
catch (...)
{
// Indicate an error has been encountered.
}
}
void Stop()
{
if (comRegistrationToken != 0)
{
CoRevokeClassObject(comRegistrationToken);
}
waitHandle.Set();
}
private:
DWORD comRegistrationToken;
EventWaitHandle waitHandle;
};
// Background task registration code.
class SampleTaskRegistrar
{
public static void Register()
{
bool taskRegistered = false;
std::wstring sampleTaskName = L"SampleTask";
auto allTasks = BackgroundTaskRegistration::AllTasks();
for (auto const& task : allTasks)
{
if (task.Value().Name() == sampleTaskName)
{
taskRegistered = true;
break;
}
}
if (!taskRegistered)
{
BackgroundTaskBuilder builder;
builder.Name(sampleTaskName);
builder.SetTaskEntryPointClsid(__uuidof(SampleTask));
builder.SetTrigger(TimeTrigger(15, false));
}
try
{
auto task = builder.Register();
}
catch (...)
{
// Indicate an error was encountered.
}
}
}
}
using namespace PackagedWinMainBackgroundTaskSample;
// Application entry point.
int wmain(_In_ int argc, _In_reads_(argc) const wchar** argv)
{
unsigned int argumentIndex;
winrt::init_apartment();
if (argc <= 1)
{
return E_INVALIDARG;
}
for (argumentIndex = 0; argumentIndex < argc ; argumentIndex += 1)
{
if (_wcsnicmp(L"RegisterSampleTask",
argv[argumentIndex],
wcslen(L"RegisterSampleTask")) == 0)
{
SampleTaskRegistrar::Register();
}
if (_wcsnicmp(L"StartSampleTaskServer",
argv[argumentIndex],
wcslen(L"StartSampleTaskServer")) == 0)
{
SampleTaskServer sampleTaskServer;
sampleTaskServer.Start();
}
}
return S_OK;
}
Comentarios
A diferencia de las aplicaciones para UWP que pueden ejecutar tareas en segundo plano en espera modernas, las aplicaciones Win32 no pueden ejecutar código desde las fases de potencia inferior de la espera moderna. Consulte Modern Standby (Espera moderna) para obtener más información.
[! NOTA] Descargue el ejemplo de tarea en segundo plano com de Win32 para ver ejemplos de código similares en el contexto de una aplicación completa de Puente de dispositivo de escritorio que usa tareas en segundo plano.
Consulte los siguientes temas relacionados para la referencia de API, instrucciones conceptuales de tareas en segundo plano y instrucciones más detalladas para escribir aplicaciones que usan tareas en segundo plano.
Temas relacionados
- Responder a eventos del sistema con tareas en segundo plano
- Registrar una tarea en segundo plano
- Establecer condiciones para ejecutar una tarea en segundo plano
- Usar un desencadenador de mantenimiento
- Controlar una tarea en segundo plano cancelada
- Supervisar el progreso y la finalización de tareas en segundo plano
- Ejecutar una tarea en segundo plano en un temporizador
- Cree y registre una tarea en segundo plano en proceso.
- Convertir una tarea en segundo plano fuera de proceso en una tarea en segundo plano dentro del proceso
Guía de tareas en segundo plano
- Directrices para tareas en segundo plano
- Depurar una tarea en segundo plano
- Cómo desencadenar eventos de suspensión, reanudación y en segundo plano en aplicaciones para UWP (al depurar)
Referencia de API de tareas en segundo plano