Criar e registrar uma tarefa em segundo plano COM Win32
Dica
O método BackgroundTaskBuilder.SetTaskEntryPointClsid está disponível a partir do Windows 10, versão 2004.
Observação
Esse cenário só é aplicável a aplicativos Win32 empacotados. Os aplicativos UWP encontrarão erros ao tentar implementar esse cenário.
APIs importantes
Crie uma classe de tarefa em segundo plano COM e registre-a para ser executada em seu aplicativo Win32 empacotado de confiança total em resposta a gatilhos. Você pode usar tarefas em segundo plano para fornecer funcionalidade quando seu aplicativo estiver suspenso ou não estiver em execução. Este tópico demonstra como criar e registrar uma tarefa em segundo plano que pode ser executada no processo do aplicativo em primeiro plano ou em outro processo.
Criar a classe Background Task
Você pode executar código em segundo plano escrevendo classes que implementam a interface IBackgroundTask . Esse código é executado quando um evento específico é disparado usando, por exemplo, SystemTrigger ou TimeTrigger.
As etapas a seguir mostram como escrever uma nova classe que implementa a interface IBackgroundTask e adicioná-la ao processo principal.
- Consulte estas instruções para fazer referência às APIs do WinRT em sua solução de aplicativo Win32 empacotada. Isso é necessário para usar o IBackgroundTask e APIs relacionadas.
- Nessa nova classe, implemente a interface IBackgroundTask . O método IBackgroundTask.Run é um ponto de entrada necessário que será chamado quando o evento especificado for disparado; esse método é necessário em todas as tarefas em segundo plano.
Observação
A própria classe de tarefa em segundo plano — e todas as outras classes no projeto de tarefa em segundo plano — precisam ser públicas.
O código de exemplo a seguir mostra uma classe de tarefa básica em segundo plano que conta primos e a grava em um arquivo até que seja solicitado que ele seja cancelado.
O exemplo do C++/WinRT implementa a classe de tarefa em segundo plano como uma coclasse COM.
Exemplo de código de tarefa em 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;
}
};
}
Adicionar o código de suporte para instanciar a classe COM
Para que a tarefa em segundo plano seja ativada em um aplicativo Win32 de confiança total, a classe de tarefa em segundo plano deve ter um código de suporte de modo que o COM entenda como iniciar o processo do aplicativo se ele não estiver em execução e, em seguida, entenda qual instância do processo é atualmente o servidor para lidar com novas ativações para essa tarefa em segundo plano.
- O COM precisa entender como iniciar o processo do aplicativo se ele ainda não estiver em execução. O processo do aplicativo que hospeda o código da tarefa em segundo plano precisa ser declarado no manifesto do pacote. O código de exemplo a seguir mostra como o SampleTask é hospedado dentro SampleBackgroundApp.exe. Quando a tarefa em segundo plano é iniciada quando nenhum processo está em execução, SampleBackgroundApp.exe será iniciada com argumentos de processo "-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>
- Depois que o processo for iniciado com os argumentos corretos, ele deverá informar ao COM que ele é o servidor COM atual para novas instâncias de SampleTask. O código de exemplo a seguir mostra como o processo de inscrição deve se registrar no COM. Observe que esses exemplos indicam como o processo se declararia como o servidor COM para SampleTask para que pelo menos uma instância fosse concluída antes de sair. Isso é opcional e lidar com uma tarefa em segundo plano pode iniciar suas principais funções de processo.
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 a tarefa em segundo plano a ser executada
- Descubra se a tarefa em segundo plano já está registrada iterando por meio da propriedade BackgroundTaskRegistration.AllTasks. Essa etapa é importante; se o aplicativo não verificar se há registros de tarefas em segundo plano existentes, ele poderá registrar facilmente a tarefa várias vezes, causando problemas de desempenho e maximizando o tempo de CPU disponível da tarefa antes que o trabalho possa ser concluído. Um aplicativo é livre para usar o mesmo ponto de entrada para lidar com todas as tarefas em segundo plano e usar outras propriedades, como o Nome ou TaskId atribuído a um BackgroundTaskRegistration, para decidir qual trabalho deve ser feito.
O exemplo a seguir itera na propriedade AllTasks e define uma variável de sinalizador como true se a tarefa já estiver 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.
- Se a tarefa em segundo plano ainda não estiver registrada, use BackgroundTaskBuilder para criar uma instância de sua tarefa em segundo plano. O ponto de entrada da tarefa deve ser o nome da classe de tarefa em segundo plano prefixada pelo namespace.
O gatilho de tarefa em segundo plano controla quando a tarefa em segundo plano será executada. Para obter uma lista de possíveis gatilhos, consulte o namespace Windows.ApplicationModel.Background.
Observação
Há suporte apenas para um subconjunto de gatilhos para tarefas em segundo plano do Win32 empacotadas.
Por exemplo, esse código cria uma nova tarefa em segundo plano e a define para executá-la em um TimeTrigger recorrente 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.
- Você pode adicionar uma condição para controlar quando sua tarefa será executada após a ocorrência do evento de gatilho (opcional). Por exemplo, se você não quiser que a tarefa seja executada até que a Internet esteja disponível, use a condição InternetAvailable. Para obter uma lista de condições possíveis, consulte SystemConditionType.
O código de exemplo a seguir atribui uma condição que exige que o usuário esteja 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 a tarefa em segundo plano chamando o método Register no objeto BackgroundTaskBuilder . Armazene o resultado BackgroundTaskRegistration para que ele possa ser usado na próxima etapa. Observe que a função de registro pode retornar erros na forma de exceções. Certifique-se de chamar Register em um try-catch.
O código a seguir registra a tarefa em segundo plano e armazena o resultado:
try
{
var task = builder.Register();
}
catch (...)
{
// Indicate an error was encountered.
}
try
{
auto task = builder.Register();
}
catch (...)
{
// Indicate an error was encountered.
}
Juntando tudo
Os exemplos de código a seguir mostram o código completo necessário para executar e registrar sua tarefa em segundo plano do COM Win32:
Concluir o manifesto do pacote do aplicativo 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>
Exemplo completo de código de tarefa em 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
{
// 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;
}
Comentários
Ao contrário dos aplicativos UWP que podem executar tarefas em segundo plano no modo de espera moderno, os aplicativos Win32 não podem executar código das fases de energia mais baixas do modo de espera moderno. Consulte Modo de espera moderno para saber mais.
[! NOTE] Baixe o exemplo de tarefa em segundo plano do Win32 COM para ver exemplos de código semelhantes no contexto de um aplicativo de Ponte de Desktop completo que usa tarefas em segundo plano.
Consulte os tópicos relacionados a seguir para obter referência de API, diretrizes conceituais de tarefas em segundo plano e instruções mais detalhadas para escrever aplicativos que usam tarefas em segundo plano.
Tópicos relacionados
- Responder a eventos do sistema com tarefas em segundo plano
- Registrar uma tarefa em segundo plano
- Definir condições para executar uma tarefa em segundo plano
- Usar um gatilho de manutenção
- Tratar uma tarefa em segundo plano cancelada
- Monitorar o progresso e a conclusão de tarefas em segundo plano
- Executar uma tarefa em segundo plano em um temporizador
- Crie e registre uma tarefa em segundo plano em processo.
- Converter uma tarefa em segundo plano fora do processo em uma tarefa em segundo plano em processo
Diretrizes de tarefas em segundo plano
- Diretrizes para tarefas em segundo plano
- Depurar uma tarefa em segundo plano
- Como disparar eventos de suspensão, retomada e em segundo plano em aplicativos UWP (durante a depuração)
Referência da API de tarefa em segundo plano